euler22-1.hsではmatchNamespickNamesとを
相互再帰で呼び出していたのだが、より簡潔に書く方法を教えてもらったので試してみた。
おお、短い。
何がどうなっているのか細かく見ていこう。
euler22-1.hsでは使用していなかったfmapData.List.unfoldrを上手く使っているようだ。
Data.List.unfoldrはあまり馴染みが無かったが、foldrの相対となる関数で
ある関数と何らかの入力値からリストを展開する関数のようだ。
> :t unfoldr
unfoldr :: (b -> Maybe (a, b)) -> b -> [a]
関数がJustを返すうちはリストの要素を作り続け、Nothingを返した時点で終了するので、
常にJustを返す関数を与えると無限リストを生成する。
> take 10 $ unfoldr (\x -> Just (x, x + 1)) 0
[0,1,2,3,4,5,6,7,8,9]
fは正規表現を適用して部分一致したリストと一致した文字列以降の残りの文字列を返すので、
> let f ns = fmap (\(_, _, x, xs) -> (xs, x)) $ matchRegexAll (mkRegex "\"([A-Z]+)\"") ns
> f "\"AAA\",\"BBB\",\"CCC\""
Just (["AAA"],",\"BBB\",\"CCC\"")
これとnames.txtの内容をunfoldrに与えると、正規表現にマッチする文字列のリストを作ってくれる。
マッチしなくなればText.Regex.matchRegexAllがNothingを返すので、リストの生成は終了して有限リストが返る。
> let listNames names = concat $ unfoldr f names
> listNames "\"AAA\",\"BBB\",\"CCC\""
["AAA","BBB","CCC"]
なるほど!

その他、foldlで自前でスコアを足し合わせていた2箇所をsumを使うようにした。
特にscoreSumのほうは、euler22-1.hsではインデックスをインクリメントしつつスコアの合計値も取り、
最後にインデックスを捨てるという少し煩雑な処理をしていたが、
インデックスを1から始まる無限リストとして扱うことで簡潔に書けるようになった。

Copyright© 2011-2021 Shunsuke Otani All Right Reserved .