2008年10月14日火曜日

Haskell で、繰り返しのある数値を生成する関数を定義 – read, cycle 関数を使って

1. 繰り返しのある数値を生成したい

Haskell で、与えられた数値を、繰り返す関数を定義したい。

例えば、1234567890 を与えたら、

`1234567890123456790123…’

のように、数値を繰り返した結果を得たい。

 

2. はじめに思い付いた面倒な方法

はじめに思い付いた方法は、

  1. 0 ~ 9 のリストを指定した回数だけ複製したものを連結して、
  2. Integer 型に変換する makeNum 関数を作成すること。

makeNum 関数の中では、次のようなリストが渡されたら、

  • [0,1,2,3,4,5]

同じ長さの、 0 からはじまって 1 ずつ増加する数列を作り、

  • [0,1,2,3,4,5] → 反転させ  [5,4,3,2,1,0]

zip でくっつける。

  • [(0,5), (1,4), (2,3), (3,2), (4,1), (5,0)]

それぞれの要素を (a,b) とすると、a*10^b したものを foldl1 で足し合わせるという方法。

makeNum :: [Integer] -> Integer
makeNum xs = foldl1 (+) [a*10^b | (a,b) <- zip xs $ reverse [0..(length xs)-1]]

main = do
  print $ makeNum $ concat $ replicate 3 [0..9]

この方法は、わかりずらい… (@_@;)

 

3. Python で実装するには

Python だったら、どう定義するか考えた。

def makeNum(L):
    return long("".join(str(x) for x in L))
    
print makeNum(range(0,10) * 3)

数値のリストを渡したら、空文字で join して long() で数値へ変換。

 

4. Haskell の Read クラスを使う

上記のPython で書いたように、

文字列から数値に変換する

ことができたら、はじめに考えた方法よりも、簡単に実装できそう。

「あれ?でもそんな関数ってあったかな?」

と思い検索してみると、Re: string to Integer に、

Turning a string into an integer is easy with the Prelude function 'read':

n :: Integer
n = read "-34232"

あらら、Prelude にあったとは気がついてなかった… ^^;

Prelude にある

を読むと、文字列 <ー> 型 の変換をするため方法が解説されている。

これによると、

Show <-> Read

  • 文字列への変換のための Show クラス
  • 文字列からの変換のための Read クラス

Showクラス については、以下を参照。

 

read メソッドの型

read 関数を見ると、最後の型が、型変数 a となっており、Read クラスの制約が付いている。

 read :: Read a => String -> a

これは、

で見た形と同じ。

 read メソッドを使えば、

  print $ (read $ concatMap show $ concat $ replicate 3 [0..9] :: Integer)

 

5. cycle 関数でリストを繰り返す

`1234567890123456790123…’  を延々と繰り返される無限リストのように見立てる。そこから、一部を取り出す関数を考える。与えられた引数を延々と繰り返す infstr 関数を定義するなら、

infstr :: String -> String
infstr cs = cs ++ infstr(cs)

main = do
  print $ (read $ take 30 $ infstr "1234567890" :: Integer)

しかし、こんな基本的な関数、どこかに定義してあるのではないかと思い Prelude を探すと、

repeat があった。しかし、これは対象がリストではなない。

repeat 関数の近くに、cycle 関数があった。

cycle :: [a] -> [a]

これを使えば、

  print $ (read $ take 30 $ cycle "1234567890" :: Integer)   

と極めてシンプルに定義できる。

一体全体、最初何を書いていたのやら。(o_ _)o~†

 

cycle 関数の定義で気になったこと

ところで、cycle の実装を見ると、

cycle                   :: [a] -> [a]
cycle []  = error "Prelude.cycle: empty list"
cycle xs  = xs' where xs' = xs ++ xs'

xs’ = where … と書くのは、なにか理由があるのかな ?

1コメント:

匿名 さんのコメント...

Exceptional post however , I was wondering if you could write a litte more on this subject?
I'd be very thankful if you could elaborate
a little bit further. Thank you!

My webpage: search engine