1

私はこれに戸惑っています。これはHaskellのループのようなもので、書き方がわかりません。基本的に、splitriffleshuffleの3つの関数を定義しました。

split :: [a] -> ([a],[a])
split xs = splitAt (length xs `div` 2) xs

riffle :: [a] -> [a] -> [a]
riffle xs [] = xs
riffle [] ys = ys
riffle (x:xs) (y:ys) = x:y:riffle xs ys

shuffle :: Int -> [a] -> [a]
shuffle 0 xs = xs
shuffle n xs = shuffle (n-1) (riffle a b)
    where (a, b) = split xs 

基本的にsplitはリストを半分に分割するだけで、riffleは2つのリストを「インターレース」することになっているので、たとえば次のようになります。

riffle [1,2,3] [4,5,6] = [1,4,2,5,3,6]

そしてシャッフルは、リストアイテムの分割とリフリングの量を繰り返すことです。次に、元のリストを再度取得するために必要なシャッフルの反復回数を出力する関数repeatsを定義する必要があります。関数は次のように定義されます。

repeats :: [Int] -> Int

シャッフルをループする方法に固執しています...リスト内包表記と関係があると思いますが、何も得られませんでした。ラムダ式はまだ試していませんが、必要ないと思います。ちなみに、シャッフルは偶数のリストで行う必要があります。何か案は?

4

3 に答える 3

12

これを解決する1つの方法は、怠惰を利用iterateして、入力の反復シャッフルの無限リストを生成するために使用することです。

> iterate (uncurry riffle . split) "ABCDEF"
["ABCDEF","ADBECF","AEDCBF","ACEBDF","ABCDEF","ADBECF","AEDCBF","ACEBDF", ...]

リストの最初の要素は元の要素なので、それをで削除してから、元の要素とは異なる要素を取得するためtailに使用takeWhileします。

> takeWhile (/= "ABCDEF") . tail $ iterate (uncurry riffle . split) "ABCDEF"
["ADBECF","AEDCBF","ACEBDF"]

ここで、lengthそのリストを取得して1つ追加するだけで、必要な数のシャッフルを取得できます。

于 2012-05-01T16:46:19.527 に答える
5

多くの場合、「ループ」ではなく無限リストを使用できます。これはそのうちの1つです。

プレリュード関数「反復」は、関数を値に繰り返し適用するため、(メモリから)

iterate f x = [x, f x, f (f x), f (f (f x)) ....]

したがって、「反復シャッフル」を開始リストに適用すると、プログレッシブシャッフルが得られます。次に、takeWhileを使用して、開始点に等しいリストの最初のエントリを見つけ、次に「長さ」を使用して、その長さを調べます。

于 2012-05-01T16:44:53.557 に答える
3

Haskellでは、反復はループではなく再帰を使用して最も一般的に表現されます。

これは多くの場合、反復の実行方法を示す内部関数を使用し、適切な引数を使用して内部関数を呼び出すことによって行われます。

おそらく、次のコードのギャップを埋めることができますか?

repeats xs = iter 1 (...) where
    iter n ys = if ys == xs
        then n
        else iter (...) (...)

iterate別のアプローチは、Haskellの怠惰を利用し、最初の引数に関数を繰り返し適用する高階関数を使用して、無限のリストでそれを行うことです。

repeats xs = (...) $ takeWhile (...) $ iterate (shuffle 1) (...)

無限リストを返しますがiterate、それの有限部分のみを使用するため、無限ループにはなりません。

于 2012-05-01T16:51:01.657 に答える