Data.List に unfoldr という関数が定義されています。どういう使い道なのだろうと思ったのですが、数を2進数文字列に変換できるのではないかと思いやってみました。
- Data.Listの例
--A simple use of unfoldr: unfoldr (\b -> if b == 0 then Nothing else Just (b, b-1)) 10 -- > [10,9,8,7,6,5,4,3,2,1]
- 2進数文字列を作ってみます。
> :t unfoldr unfoldr :: (b -> Maybe (a, b)) -> b -> [a] - ループが終了するときは Nothing を返す - a はリストの要素になるのだからbを2で割った余り。
- foldr なんだから[1,0,0,0]になると思ったら逆だった・・・rz > unfoldr (\b -> if b == 0 then Nothing else Just (b `mod` 2 , b `div` 2)) 8 -- > [0,0,0,1] > unfoldr (\b -> if b == 0 then Nothing else Just (b `mod` 2 + ord '0', b `div` 2)) 8 -- > [48,48,48,49] > map chr $ unfoldr (\b -> if b == 0 then Nothing else Just (b `mod` 2 + ord '0', b `div` 2)) 8 -- > "0001" > reverse $ map chr $ unfoldr (\b -> if b == 0 then Nothing else Just (b `mod` 2 + ord '0', b `div` 2)) 8 --> "1000" > reverse $ map chr $ unfoldr (\b -> if b == 0 then Nothing else Just (b `mod` 2 + ord '0', b `div` 2)) 65535 -- > "1111111111111111"
- 2進数文字列を数字に変換
> :t foldr foldr :: (a -> b -> b) -> b -> [a] -> b
foldr、foldlを使うときはいちいち型を表示させてパラメータの順序を調べています。
> let binSTR2Int s =let (_,d)=foldr (\x (n,acc) -> (n*2,acc+((ord x) - 48)*n)) (1,0) s in d > binSTR2Int "111" --> 7 > binSTR2Int "10000" -- >16
import Data.List import Data.Char binSTR2Int :: String -> Int binSTR2Int s =let (_,d)=foldr accumulate (1,0) s in d where accumulate c (n,acc) | i==c0 || i==c1 = (n*2,acc+(i - c0)*n) | otherwise = error "invalid bin string sequence" where i = ord c c0 = ord '0' c1 = ord '1'