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