Inverse Fizzbuzz を Squeak Smalltalk で
@yuroyoro 経由で
とりあえずブルートフォース版を Squeak Smalltalk に写してみた。PartialFunction はスルー。
| fizzbuzz all results io inverses answer | fizzbuzz := [:n | | str | str := ''. (n isDivisibleBy: 3) ifTrue: [str := str, 'fizz']. (n isDivisibleBy: 5) ifTrue: [str := str, 'buzz']. str asSymbol]. all := (1 to: 100) gather: [:x | (x to: 100) collect: [:y | {x. y}]]. results := all collect: [:x | ((x first to: x second) collect: fizzbuzz) copyWithout: '']. io := all with: results collect: [:x :y | {x. y}]. inverses := io groupBy: [:x | x second] having: #notEmpty. answer := (inverses keys collect: [:k | k -> ((inverses at: k) detectMin: [:x | x first last - x first first]) first]) as: Dictionary. answer at: #(fizz fizz buzz) "=> #(6 10) "
ふうつに書き直すとこんな感じに。
| fizzbuzz answer | fizzbuzz := [:n | | str | str := ''. (n isDivisibleBy: 3) ifTrue: [str := str, 'fizz']. (n isDivisibleBy: 5) ifTrue: [str := str, 'buzz']. str asSymbol]. answer := Dictionary new. (1 to: 100) do: [:x | (x to: 100) do: [:y | | fizzbuzzes | fizzbuzzes := ((x to: y) collect: fizzbuzz) copyWithout: ''. (answer at: fizzbuzzes ifAbsentPut: [x to: y]) size > (x to: y) size ifTrue: [answer at: fizzbuzzes put: (x to: y)]]]. answer at: #(fizz fizz buzz) "=> (6 to: 10) "
[追記]
とりあえず元ネタ本文中にある「Lets do a few simple examples」でのやりとりのすべての例+αを正しく返せるように、しかしブルートフォースよりは効率重視で書いてみました。
| fizzbuzz cycle2N fizzbuzzes values cycleSize answer invFizzbuzz | fizzbuzz := [:n | | str | str := ''. (n isDivisibleBy: 3) ifTrue: [str := str, 'fizz']. (n isDivisibleBy: 5) ifTrue: [str := str, 'buzz']. str asSymbol]. cycle2N := (3 * 5) * 2. fizzbuzzes := (1 to: cycle2N) collect: fizzbuzz thenSelect: #notEmpty. values := (1 to: cycle2N) select: [:n | (fizzbuzz value: n) notEmpty]. cycleSize := values size / 2. answer := Dictionary new. (1 to: cycleSize) do: [:start | (1 to: cycleSize) + start - 1 do: [:end | | subs range | subs := fizzbuzzes copyFrom: start to: end. range := (values at: start) to: (values at: end). (answer at: subs ifAbsentPut: [range]) size > range size ifTrue: [answer at: subs put: range]]]. invFizzbuzz := [:seq | (seq size <= cycleSize ifTrue: [answer at: seq ifAbsent: [nil]] ifFalse: [ | range starting | range := answer at: (seq first: cycleSize) ifAbsent: [nil]. starting := values indexOf: range last. ((seq allButFirst: cycleSize) allSatisfy: [:each | each = (fizzbuzzes atWrap: (starting := starting + 1))]) ifTrue: [(range first to: (values atWrap: starting) + (starting - 1 // values size * cycle2N)) asArray] ifFalse: [nil]]) ifNotNil: [:intvl | intvl asArray]]. self assert: (invFizzbuzz value: #(fizz)) = #(3). self assert: (invFizzbuzz value: #(buzz)) = #(5). self assert: (invFizzbuzz value: #(fizz buzz)) = #(9 10). self assert: (invFizzbuzz value: #(fizz buzz fizz)) = #(3 4 5 6). self assert: (invFizzbuzz value: #(buzz fizz buzz)) = nil. self assert: (invFizzbuzz value: #(fizz fizz)) = #(6 7 8 9). self assert: (invFizzbuzz value: #(fizz fizz buzz)) = #(6 7 8 9 10). self assert: (invFizzbuzz value: #(fizzbuzz fizz buzz fizz fizz buzz fizz fizzbuzz fizz)) = (15 to: 33) asArray
同じようなことを別のアプローチで。
| invFizzbuzz | invFizzbuzz := [:seq | | fizzbuzzes instream offset start values min | fizzbuzzes := #(fizz buzz fizz fizz buzz fizz fizzbuzz) readStream. values := #(3 5 6 9 10 12 15). instream := seq readStream. min := nil. [:exit | [ offset := 0. [fizzbuzzes atEnd or: [fizzbuzzes peek = instream peek]] whileFalse: [fizzbuzzes next]. fizzbuzzes atEnd ifTrue: [exit value]. start := fizzbuzzes position + 1. [instream atEnd not and: [fizzbuzzes next = instream peek]] whileTrue: [instream next. fizzbuzzes atEnd ifTrue: [fizzbuzzes reset. offset := offset + 1]]. fizzbuzzes position = 0 ifTrue: [offset := offset - 1]. instream atEnd ifTrue: [ | new | new := (values at: start) to: (values atWrap: fizzbuzzes position) + (offset * 15). min := {min ifNil: [1 to: SmallInteger maxVal]. new} detectMin: #size]. instream reset. fizzbuzzes reset; position: start ] repeat ] valueWithExit. min ]. self assert: (invFizzbuzz value: #(fizz)) = #(3). self assert: (invFizzbuzz value: #(buzz)) = #(5). self assert: (invFizzbuzz value: #(fizz buzz)) = #(9 10). self assert: (invFizzbuzz value: #(fizz buzz fizz)) = #(3 4 5 6). self assert: (invFizzbuzz value: #(buzz fizz buzz)) = nil. self assert: (invFizzbuzz value: #(fizz fizz)) = #(6 7 8 9). self assert: (invFizzbuzz value: #(fizz fizz buzz)) = #(6 7 8 9 10). self assert: (invFizzbuzz value: #(fizzbuzz fizz buzz fizz fizz buzz fizz fizzbuzz fizz)) = (15 to: 33) asArray
[追記2]
ジェネレーターを使ったまた別のアプローチで。
| fizzbuzz invFizzbuzz | fizzbuzz := [:n | | str | str := ''. (n isDivisibleBy: 3) ifTrue: [str := str, 'fizz']. (n isDivisibleBy: 5) ifTrue: [str := str, 'buzz']. str asSymbol]. invFizzbuzz := [:seq | | gen prev min | gen := Generator on: [:g | 1 to: Float infinity do: [:n | (fizzbuzz value: n) ifNotEmptyDo: [:fb | g yield: fb->n]]]. min := nil. prev := 0. [:exit | [ | range | gen reset. [gen peek value > prev] whileFalse: [gen next value > 30 ifTrue: [exit value]]. [gen peek key = seq first] whileFalse: [gen peek value > 30 ifTrue: [exit value] ifFalse: [gen next]]. prev := gen peek value. ((range := gen next: seq size) collect: #key) asArray = seq ifTrue: [ | new | new := range first value to: range last value. min := {min ifNil: [1 to: SmallInteger maxVal]. new} detectMin: #size] ] repeat ] valueWithExit. min]. self assert: (invFizzbuzz value: #(fizz)) = #(3). self assert: (invFizzbuzz value: #(buzz)) = #(5). self assert: (invFizzbuzz value: #(fizz buzz)) = #(9 10). self assert: (invFizzbuzz value: #(fizz buzz fizz)) = #(3 4 5 6). self assert: (invFizzbuzz value: #(buzz fizz buzz)) = nil. self assert: (invFizzbuzz value: #(fizz fizz)) = #(6 7 8 9). self assert: (invFizzbuzz value: #(fizz fizz buzz)) = #(6 7 8 9 10). self assert: (invFizzbuzz value: #(fizzbuzz fizz buzz fizz fizz buzz fizz fizzbuzz fizz)) = (15 to: 33) asArray