“サイコロの展開図”問題を正規表現がデフォで使える Pharo Smalltalk で(正規表現を使わずに Squeak でも)


Pharo では正規表現が使えるとのことなので、ちょうどそれ向きのお題ということもあり、久しぶりに Pharo で、Squeak 向けのコードの動作確認の範疇をこえて、書き下ろしていろいろ遊んでみました。

| patterns solveDiceDev |

patterns := #(
     '12S/453'
     '1T6/D45'
     '146/53D'
     '15S/3D4'
     '215/T64'
     '2S5/41T'
) inject: OrderedCollection new into: [:sum :pat |
     | alt |
     alt := (pat last: 3), '/', (pat first: 3).
     sum addAll: {pat. pat reversed. alt. alt reversed}; yourself
].

solveDiceDev := [:probStr |
   | boxes regex found |
   boxes := probStr allRegexMatches: '(w|x|y|z)'.
   regex := (probStr copyWithRegex: '(w|x|y|z)' matchesReplacedWith: '(.)') asRegex.
   found := OrderedCollection new.
   patterns do: [:pat |
      (regex matches: pat) ifTrue: [
         found add: (String streamContents: [:ss |
            (1 to: boxes size) do: [:idx |
               ss nextPutAll: (boxes at: idx), '=', (regex subexpression: idx+1)
            ] separatedBy: [ss nextPutAll: ',']
         ])
      ]
   ].
   found size caseOf: {
      [0] -> ['none'].
      [1] -> [found first].
   } otherwise: ['many']
].

#(
   '0' 'Tx4/5yz' 'x=1,y=S,z=2'
   '1' '14S/xyz' 'none'
   '2' '1w6/xyz' 'many'
   '3' '4w3/12S' 'w=5'
   '4' '4w3/S51' 'w=D'
   '5' '15S/wD4' 'w=3'
   '6' '54D/6Tw' 'w=1'
   '7' 'S21/35w' 'w=4'
   '8' 'w2x/354' 'w=S,x=1'
   '9' 'wx1/54D' 'w=6,x=T'
   '10' '45w/12x' 'w=3,x=S'
   '11' '5w2/x14' 'w=S,x=T'
   '12' 'Dw5/x41' 'w=3,x=6'
   '13' 'w4x/1y6' 'w=D,x=5,y=T'
   '14' '15w/xy4' 'w=S,x=3,y=D'
   '15' 'D35/wxy' 'w=6,x=4,y=1'
   '16' '4wx/51y' 'w=6,x=T,y=2'
   '17' 'wTx/D4y' 'w=1,x=6,y=5'
   '18' 'wxy/z3D' 'w=1,x=4,y=6,z=5'
   '19' 'wx5/1yz' 'w=D,x=4,y=T,z=6'
   '20' 'w53/xyz' 'w=4,x=1,y=2,z=S'
   '21' 'wx1/yzD' 'w=6,x=T,y=5,z=4'
   '22' 'wxS/3yz' 'w=1,x=5,y=D,z=4'
   '23' 'wx2/y1z' 'w=5,x=S,y=T,z=4'
   '24' '4wx/2yz' 'w=1,x=T,y=S,z=5'
   '25' 'T6w/xyz' 'w=4,x=2,y=1,z=5'
   '26' 'Swx/yDz' 'w=5,x=1,y=4,z=3'
   '27' 'wDx/yzS' 'w=3,x=4,y=1,z=5'
   '28' 'wxy/5Sz' 'w=T,x=1,y=4,z=2'
   '29' 'wSx/4yz' 'w=2,x=5,y=1,z=T'
   '30' 'wxS/y5z' 'w=1,x=2,y=4,z=3'
   '31' 'wxy/35z' 'w=S,x=2,y=1,z=4'
   '32' 'wxy/T6z' 'w=2,x=1,y=5,z=4'
   '33' 'wxD/yz1' 'w=5,x=4,y=6,z=T'
   '34' '1wx/yz5' 'w=T,x=6,y=D,z=4'
   '35' 'wx3/y5z' 'w=4,x=D,y=S,z=1'
   '36' '6wx/y3z' 'w=4,x=1,y=D,z=5'
   '37' '5wx/4yz' 'w=1,x=2,y=6,z=T'
   '38' 'wx4/Syz' 'w=3,x=5,y=2,z=1'
   '39' 'w3D/xyz' 'w=5,x=1,y=4,z=6'
   '40' 'w3x/6yz' 'w=D,x=5,y=4,z=1'
   '41' 'wxy/z12' 'w=4,x=6,y=T,z=5'
   '42' '1wS/xyz' 'many'
   '43' 'wxy/Dz5' 'many'
   '44' '3w4/xyz' 'many'
   '45' 'wxy/5zD' 'many'
   '46' 'wxy/Tz4' 'many'
   '47' '5wD/xyz' 'many'
   '48' 'wDx/y5z' 'many'
   '49' 'wxy/3z4' 'many'
   '50' 'wxy/5z2' 'many'
   '51' 'Dyz/S1x' 'none'
   '52' 'w1z/xyS' 'none'
   '53' '15x/T6y' 'none'
   '54' 'zy4/5x6' 'none'
   '55' '2xy/4Tz' 'none'
   '56' 'xzS/y1w' 'none'
   '57' 'Syx/4z5' 'none'
   '58' 'xwS/Tzy' 'none'
   '59' 'D5z/xwy' 'none'
   '60' 'yxD/z35' 'none'
) groupsOf: 3 atATimeDo: [:data | self assert: [(solveDiceDev value: data second) = data third]]


Pharo も 4 まで来てだいぶマシになったように思うのですが、4 になって改めて、ifTrue: ifFalse: のイッパツ挿入とか、advance argument(shift + alt + A)とか、これなくして Pharo 使いはいったいどうやってコードを書いているの?といったような、いにしえの Smalltalk-80 〜 Squeak 時代の(かろうじて Pharo 3 までは使えた)便利機能をぶっ壊したまま放置されているところが多々あり、相変わらずいろいろダメなやつですね。

もちろん、今回の正規表現とかコード補完とか、今日日の処理系や IDE にはあってあたりまえの機能があたりまえのようにデフォで用意されている点に関しては Squeak は完全に置いてきぼりにされた感があるので(だが、古き良き機能をぶっ壊すくらいならそれでいい…)、Squeak を知らない世代は Pharo でぜんぜんオッケー、あえて Squeak を使う意味がきっとわからないのでは?とも思いますが。^^;



そんなわけでやっぱりしばらくは Squeak 使いなので Squeak でも動く正規表現を使わない版も書きました。

| boxChars patterns solveDiceDev |

boxChars := 'wxyz'.

patterns := #(
   '12S/453'
   '1T6/D45'
   '146/53D'
   '15S/3D4'
   '215/T64'
   '2S5/41T'
) inject: OrderedCollection new into: [:sum :pat |
   | alt |
   alt := (pat last: 3), '/', (pat first: 3).
   sum addAll: {pat. pat reversed. alt. alt reversed}; yourself
].

solveDiceDev := [:probStr |
   | found |
   found := OrderedCollection new.
   patterns do: [:pat | [:exit |
      found add: (String streamContents: [:ss |
         pat with: probStr do: [:a :b |
            (a = b or: [(boxChars includes: b) and: [ss nextPutAll: {b. $=. a. $,}. true]]) ifFalse: [exit value]
         ].
         ss skip: -1
      ])
   ] valueWithExit].
   found ifEmpty: ['none'] ifNotEmpty: [found size = 1 ifTrue: [found first] ifFalse: ['many']]
].

#(
   '0' 'Tx4/5yz' 'x=1,y=S,z=2'
   '1' '14S/xyz' 'none'
   '2' '1w6/xyz' 'many'
   '3' '4w3/12S' 'w=5'
   '4' '4w3/S51' 'w=D'
   '5' '15S/wD4' 'w=3'
   '6' '54D/6Tw' 'w=1'
   '7' 'S21/35w' 'w=4'
   '8' 'w2x/354' 'w=S,x=1'
   '9' 'wx1/54D' 'w=6,x=T'
   '10' '45w/12x' 'w=3,x=S'
   '11' '5w2/x14' 'w=S,x=T'
   '12' 'Dw5/x41' 'w=3,x=6'
   '13' 'w4x/1y6' 'w=D,x=5,y=T'
   '14' '15w/xy4' 'w=S,x=3,y=D'
   '15' 'D35/wxy' 'w=6,x=4,y=1'
   '16' '4wx/51y' 'w=6,x=T,y=2'
   '17' 'wTx/D4y' 'w=1,x=6,y=5'
   '18' 'wxy/z3D' 'w=1,x=4,y=6,z=5'
   '19' 'wx5/1yz' 'w=D,x=4,y=T,z=6'
   '20' 'w53/xyz' 'w=4,x=1,y=2,z=S'
   '21' 'wx1/yzD' 'w=6,x=T,y=5,z=4'
   '22' 'wxS/3yz' 'w=1,x=5,y=D,z=4'
   '23' 'wx2/y1z' 'w=5,x=S,y=T,z=4'
   '24' '4wx/2yz' 'w=1,x=T,y=S,z=5'
   '25' 'T6w/xyz' 'w=4,x=2,y=1,z=5'
   '26' 'Swx/yDz' 'w=5,x=1,y=4,z=3'
   '27' 'wDx/yzS' 'w=3,x=4,y=1,z=5'
   '28' 'wxy/5Sz' 'w=T,x=1,y=4,z=2'
   '29' 'wSx/4yz' 'w=2,x=5,y=1,z=T'
   '30' 'wxS/y5z' 'w=1,x=2,y=4,z=3'
   '31' 'wxy/35z' 'w=S,x=2,y=1,z=4'
   '32' 'wxy/T6z' 'w=2,x=1,y=5,z=4'
   '33' 'wxD/yz1' 'w=5,x=4,y=6,z=T'
   '34' '1wx/yz5' 'w=T,x=6,y=D,z=4'
   '35' 'wx3/y5z' 'w=4,x=D,y=S,z=1'
   '36' '6wx/y3z' 'w=4,x=1,y=D,z=5'
   '37' '5wx/4yz' 'w=1,x=2,y=6,z=T'
   '38' 'wx4/Syz' 'w=3,x=5,y=2,z=1'
   '39' 'w3D/xyz' 'w=5,x=1,y=4,z=6'
   '40' 'w3x/6yz' 'w=D,x=5,y=4,z=1'
   '41' 'wxy/z12' 'w=4,x=6,y=T,z=5'
   '42' '1wS/xyz' 'many'
   '43' 'wxy/Dz5' 'many'
   '44' '3w4/xyz' 'many'
   '45' 'wxy/5zD' 'many'
   '46' 'wxy/Tz4' 'many'
   '47' '5wD/xyz' 'many'
   '48' 'wDx/y5z' 'many'
   '49' 'wxy/3z4' 'many'
   '50' 'wxy/5z2' 'many'
   '51' 'Dyz/S1x' 'none'
   '52' 'w1z/xyS' 'none'
   '53' '15x/T6y' 'none'
   '54' 'zy4/5x6' 'none'
   '55' '2xy/4Tz' 'none'
   '56' 'xzS/y1w' 'none'
   '57' 'Syx/4z5' 'none'
   '58' 'xwS/Tzy' 'none'
   '59' 'D5z/xwy' 'none'
   '60' 'yxD/z35' 'none'
) groupsOf: 3 atATimeDo: [:data | self assert: [(solveDiceDev value: data second) = data third]]