unfoldr を使って2進数文字列を作る

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'