Haskell Bowling を Squeak の Smalltalk で

#(0 0 10 8 2 10 10 10 5 3 8 2 10 10 10 10) bowlingScore   " => 201 "


まずは Haskell 版を直訳ぎみにして、それからヒネリを入れてみよう…と思ったのですが、もとがあまりにシンプルで代替え案が思いつかなかったのでそのままです。あしからず。


下の定義はファイルイン用コードなので、コピーして Squeak システムのどこか適当な場所にペーストした後、あらためて全体を選択して fileIn selection (alt/cmd + shift + g) とするなどの操作で読み込めます。テストは open... → SUnit Test Runner から ArrayBowlingScoreTest を探して選択 → Run One してください。

'From Squeakland 3.8-05 of 7 September 2005 [latest update: #527]'!

!Array methodsFor: 'math functions' stamp: 'sumim 11/21/2006'!
bowlingScore
	self isEmpty ifTrue: [^ 0].
	self size = 1 ifTrue: [^ self first].
	self size <= 3 ifTrue: [^ self sum].
	self first = 10 ifTrue: [^ (self first: 3) sum + self allButFirst bowlingScore].
	self first + self second = 10
		ifTrue: [^ (self first: 3) sum + (self allButFirst: 2) bowlingScore].
	^ self first + self second + (self allButFirst: 2) bowlingScore
! !


TestCase subclass: #ArrayBowlingScoreTest
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Collections-Arrayed-Tests'!

!ArrayBowlingScoreTest methodsFor: 'tests' stamp: 'sumim 11/21/2006'!
test1
	self assert: #(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) bowlingScore = 0
! !

!ArrayBowlingScoreTest methodsFor: 'tests' stamp: 'sumim 11/21/2006'!
test2
	self assert: (Array new: 12 withAll: 10) bowlingScore = 300
! !

!ArrayBowlingScoreTest methodsFor: 'tests' stamp: 'sumim 11/21/2006'!
test3
	self assert: (#(10 5 5), (Array new: 17 withAll: 0)) bowlingScore = 30
! !

!ArrayBowlingScoreTest methodsFor: 'tests' stamp: 'sumim 11/21/2006'!
test4
	self assert: (Array new: 20 withAll: 3) bowlingScore = 60
! !

おまけ

Ruby にも Smalltalk の #sum、#first、#first:、#second、#allButFirst、#allButFirst: を定義して直訳気味に。

[0,0,10,8,2,10,10,10,5,3,8,2,10,10,10,10].bowling_score   #=> 201
class Array
  def bowling_score
    return 0 if empty?
    return first if size == 1
    return sum if size <= 3
    return first(3).sum + all_but_first.bowling_score if first == 10
    return first(3).sum + all_but_first(2).bowling_score if first + second == 10
    first + second + all_but_first(2).bowling_score
  end

  def sum
    inject(0) { |s,e| s+e }
  end

  def first(n=1)
    if n == 1
      self[0]
    else
      self[0, n]
    end
  end

  def second
    self[1]
  end

  def all_but_first(n=1)
    self[n, size - n + 1]
  end
end


id:sumim:20061124:p1 に続く。