3

私は最近 F# で遊んでいます。forループを使用して、リスト内の他のすべての要素と乗算される要素へのシーケンスを生成する代わりに、Seqマップ関数または同様のものを使用して以下のようなものを生成するにはどうすればよいか疑問に思っていました.

たとえば、リスト[1..10]がある場合、次のような結果を生成するfunを適用したいと思います

[(1*1); (1*2);(1*3); (1*4); (1*5)......(2*1);(2*2);(2*3).....(3*1);(3*2)...]

どうすればこれを達成できますか?.

ご協力いただきありがとうございます。

4

4 に答える 4

6
let list = [1..10]

list |> List.map (fun v1 -> List.map (fun v2 -> (v1*v2)) list) |> List.collect id

最後の List.collect は、リストのリストを平坦化します。遅延シーケンスが必要な場合は、List の代わりに Seq を使用しても同じように機能します。

または、collectcfernが提案したように、メインのイテレータとして使用し、執拗に無名関数を排除します。

let flip f x y = f y x

let list = [1..10]

list |> List.collect ((*) >> ((flip List.map) list))
于 2010-06-05T13:37:15.610 に答える
5

リスト内包表記は、これを行う最も簡単な方法です。

let allpairs L = 
    [for x in L do
        for y in L -> (x*y)]

または、ループを使用せずに:

let pairs2 L = L |> List.collect (fun x -> L |> List.map (fun y -> (x*y)))


コメントに応じて編集: 次 のように、自己交差拡張メソッドをリストに追加できます。

type Microsoft.FSharp.Collections.List<'a> with
    member L.cross f = 
        [for x in L do
            for y in L -> f x y]

例:

> [1;2;3].cross (fun x y -> (x,y));;
val it : (int * int) list =
  [(1, 1); (1, 2); (1, 3); (2, 1); (2, 2); (2, 3); (3, 1); (3, 2); (3, 3)]

私自身は F# で拡張メソッドを使用しませんが、少し C# っぽいと感じています。しかし、それは主に、F# で流暢な構文が必要だとは思わないためです。通常、関数をパイプ (|>) 演算子で連結しているためです。

私のアプローチは、タイプ自体ではなく、クロス関数で List モジュールを拡張することです。

module List =
    let cross f L1 L2 = 
        [for x in L1 do
            for y in L2 -> f x y]

これを行うと、リストの他のメソッドと同じように cross メソッドを使用できます。

> List.cross (fun x y -> (x,y)) [1;2;3] [1;2;3];;
val it : (int * int) list =
  [(1, 1); (1, 2); (1, 3); (2, 1); (2, 2); (2, 3); (3, 1); (3, 2); (3, 3)]
> List.cross (*) [1;2;3] [1;2;3];;
val it : int list = [1; 2; 3; 2; 4; 6; 3; 6; 9]
于 2010-06-05T13:38:26.543 に答える
3

または、一般的な外積関数を実装できます。

let cross l1 l2 =
  seq { for el1 in l1 do
          for el2 in l2 do
            yield el1, el2 };;

この関数を使用してジョブを完了します。

cross [1..10] [1..10] |> Seq.map (fun (a,b) -> a*b) |> Seq.toList
于 2010-06-05T16:09:27.563 に答える
2

ループなしで同じことを実装するには、Mau によって投稿された高階関数forを使用してソリューションを使用するか、再帰を使用して同じことを明示的に記述できます。

let cross xs ys =
  let rec crossAux ol2 l1 l2 =
    match l1, l2 with
    // All elements from the second list were processed
    | x::xs, [] -> crossAux ol2 xs ol2             
    // Report first elements and continue looping after
    // removing first element from the second list
    | x::xs, y::ys -> (x, y)::(crossAux ol2 l1 ys) 
    // First list is empty - we're done
    | [], _ -> []
  crossAux ys xs ys

これは、関数型プログラミングと再帰を学習している場合に役立つ可能性がありますが、シーケンス式を使用したソリューションははるかに実用的です。

List.map補足として、Mau による最初のバージョンは、 への呼び出しをlike this への呼び出しと結合できるため、もう少し良くすることができList.collect idます (ネストされた処理ラムダをパラメーターとして に直接渡すことができますcollect)。関数は次のcrossようになります (もちろん、これを変更して、タプルを作成する代わりに 2 つの数値に適用するパラメーターを取ることができます)。

let cross xs ys =
  xs |> List.collect (fun v1 -> 
    ys |> List.map (fun v2 -> (v1, v2))) 

ちなみに、私の本の無料の章が利用可能で、シーケンス式と関数がどのように機能するかを説明していますList.collectシーケンス式ではに直接対応することforに注意してください。したがって、この高階関数を使用するだけでコードを記述できます。List.collect

let cross xs ys =
  xs |> List.collect (fun v1 -> 
    ys |> List.collect (fun v2 -> [(v1, v2)] )) 

ただし、詳細については無料の章を参照してください:-)。

于 2010-06-05T22:06:39.527 に答える