1

言葉遣いが下手なので、お手柔らかにお願いします。

Haskell で、可能なすべての数値をリストのリストの形式で生成する必要がある問題を実行しています。

たとえば、x = 3 で y = 2 の場合、次のようなリストのリストを生成する必要があります。

[[1,1,1], [1,2,1], [2,1,1], [2,2,1], [1,1,2], [1,2,2], [2,1,2], [2,2,2]]

x と y が関数に渡され、ゼロ以外の正の整数 x と y を処理する必要があります。

私は完全に道に迷っており、どのように始めればよいかさえわかりません。

私を助けてくれる親切な人のために、数学の重い説明をできるだけ理解しやすいようにしてください. 私は数学が本当に苦手です。

4

2 に答える 2

3

これが宿題だと仮定して、私はあなたに答えの一部を与え、この種の問題を通して私がどのように考えるかを示します. GHCi で実験して、必要な部分を構築することは役に立ちます。必要なことの 1 つは、1 から までの数値のリストを生成できることyです。yが 7 であると仮定します。

λ> [1..7]
[1,2,3,4,5,6,7]

しかしすぐにわかるように、本当に必要なのは単純なリストではなく、その上に構築できるリストのリストです。このような:

λ> map (:[]) [1..7]
[[1],[2],[3],[4],[5],[6],[7]]

これは基本的に、配列内の各要素を取り、それを空のリストに追加することを意味します[]。これで、これを行う関数を作成できます。

makeListOfLists y = map (:[]) [1..y]

次に、リストのリスト内のすべての要素の先頭に新しい要素を追加する方法が必要です。このようなもの:

λ> map (99:) [[1],[2],[3],[4],[5],[6],[7]]
[[99,1],[99,2],[99,3],[99,4],[99,5],[99,6],[99,7]]

(ここでは、たとえば 1 の代わりに 99 を使用しました。これは、数字がどこから来たのかを簡単に確認できるようにするためです。) したがって、それを行う関数を作成できます。

prepend x yss = map (x:) yss

最終的には、リストとリストのリストを取り、リストのprependすべての要素に対してリストのリストのすべての要素を呼び出すことができるようにしたいと考えています。map関数を再度使用してそれを行うことができます。しかし、結局のところ、次のように引数の順序を に切り替えると、それを行うのが少し簡単になりますprepend

prepend2 yss x = map (x:) yss

次に、次のようなことができます。

λ>  map (prepend2 [[1],[2],[3],[4],[5],[6],[7]]) [97,98,99]
[[[97,1],[97,2],[97,3],[97,4],[97,5],[97,6],[97,7]],[[98,1],[98,2],[98,3],[98,4],[98,5],[98,6],[98,7]],[[99,1],[99,2],[99,3],[99,4],[99,5],[99,6],[99,7]]]

だから今、その関数を書くことができます:

supermap xs yss = map (prepend2 yss) xs

あなたの例を使用すると、x = 2およびy = 3の場合、必要な答えは次のとおりです。

λ> let yss = makeListOfLists 3
λ> supermap [1..3] yss
[[[1,1],[1,2],[1,3]],[[2,1],[2,2],[2,3]],[[3,1],[3,2],[3,3]]]

(それが必要なすべてであれば、リスト内包表記を使用してこれをより簡単に実行できたはずです。しかし、任意の x に対してこれを行う必要があるため、リスト内包表記は機能しません。)

ここから取得して、任意の x に拡張できることを願っています。

于 2013-02-04T12:03:00.830 に答える
1

特定の x については、既に述べたように、x が 3 に等しいと仮定して、次のように書くと、リスト内包表記がうまくいきます。

 generate y = [[a,b,c] | a<-[1..y], b<-[1..y], c <-[1..y]]

しかし、x があらかじめ決められていない場合、人生はさらに複雑になります。私は Haskell でのプログラミングの経験があまりなく、ライブラリ関数に精通しておらず、私のアプローチは最も効率的なソリューションとはほど遠いので、あまり厳しく判断しないでください。

私のソリューションは、次の 2 つの機能で構成されています。

strip [] = []
strip (h:t) = h ++ strip t

populate y 2 = strip( map (\a-> map (:a:[]) [1..y]) [1..y])
populate y x = strip( map (\a-> map (:a) [1..y]) ( populate y ( x - 1) ))

strip は、ネストされたリストに対して定義されています。リスト項目をマージすることで、いわば階層を減らします。たとえば、呼び出し

strip [[1],[2],[3]]

出力を生成します。

[1,2,3]

populate はトリッキーです。

再帰の最後のステップで、2 番目の引数が 2 に等しい場合、関数は [1..y] の各項目を同じリストのすべての要素と共に新しいリストにマップします。例えば

map (\a-> map (:a:[]) [1..2]) [1..2])

出力を生成します。

[[[1,1],[2,1]],[[1,2],[2,2]]]

そして strip 関数はそれを次のように変えます:

[[1,1],[2,1],[1,2],[2,2]]

再帰の最初のステップに関しては、x が 2 より大きい場合、populate はほとんど同じことを行いますが、今回はリストの項目を再帰呼び出しによって生成されたリストにマップします。そして最後に:

populate 2 3

望ましい結果が得られます。

[[1,1,1],[2,1,1],[1,2,1],[2,2,1],[1,1,2],[2,1,2],[1,2,2],[2,2,2]]

上で述べたように、このアプローチは最も効率的でも読みやすいものでもありませんが、問題は解決すると思います。実際、理論的には、再帰を多用せずにこれを解決する唯一の方法は、リスト内包表記を使用して文字列を作成し、その文字列を動的にコンパイルすることです。これは、プログラマーとしての私の短い経験によれば、決して良い方法ではありません。解決。

于 2013-02-04T21:46:05.953 に答える