2

代替要素を使用して、F#リストを2つに分割しようとしています。これが私の試みです:

let split l =  
    let rec loop l isEven result1 result2 =  
        match l with  
        | [] -> result1 result2  
        | [head::tail] when isEven -> loop tail (not isEven) head::result1 result2  
        | [head::tail] -> loop tail (not isEven) result1 head::result2  
    loop l false [] []

それは私にエラーを与えます:

Program.fs(5,39): error FS0001: Type mismatch. Expecting a  
    'a    
but given a  
    'b -> 'a list      
The resulting type would be infinite when unifying ''a' and ''b -> 'a list'

それがどのように無限になり得るのかわかりません。また、なぜそれが「b」から「リスト」までの関数を与えていると考えるのかわかりません。誰かが私がどこで間違っているのか教えてもらえますか?

4

3 に答える 3

6

ジャックは何が悪いのかをうまく説明しました。これは、一度に2つの要素に一致する代替ソリューションです。F#のパターンマッチングドキュメントには、多くの優れた例があります。

let split list = 
    let rec split odd even list =
        match list with
        | a::b::tail -> split (a::odd) (b::even) tail
        | a::tail -> split (a::odd) even tail
        | [] -> List.rev odd, List.rev even

    split [] [] list

出力例。

printfn "%A" (split [1 .. 10])
System.Console.ReadLine() |> ignore

([1; 3; 5; 7; 9], [2; 4; 6; 8; 10])
于 2013-01-30T19:03:56.047 に答える
5

修正されたバージョンは次のとおりです。

let rec loop l isEven result1 result2 =
    match l with
    | [] ->
        result1, result2
    | head :: tail when isEven ->
        loop tail (not isEven) (head :: result1) result2
    | head :: tail ->
        loop tail (not isEven) result1 (head :: result2)
  • 最初のケース( )では、関数が値をタプルとして返す必要が[]あるため、コンマを追加しました。loopカンマがないと、基本的result1に関数のように扱い、それに適用result2されます。
  • 空のリストパターンは正しい([])でしたが、それ以外の場合は角かっこを使用せず、短所(::)パターンのみを使用します。
  • 括弧で囲む必要がありhead :: resultます。そうでない場合、F#は次のようにコードを読み取ります(loop tail (not isEven) head) :: (result1 result2)

ああ、そしてあなたが返すリストを元のリストと同じ順序にしたいのなら、あなたは次のようにリストを返すときにList.revを使う必要があります:

match l with
| [] ->
    List.rev result1, List.rev result2

isEven最後に、関数を少し簡略化したバージョンを示します。関数を機能させるためにパラメーターは実際には必要ありません。代わりに、リストを同じ長さに保つようにしてください。

let rec loop (result1, result2) l =
    match l with
    | [] ->
        List.rev result1, List.rev result2
    | hd :: tl ->
        if List.length result1 = List.length result2 then
            loop (hd :: result1, result2) tl
        else
            loop (result1, hd :: result2) tl
于 2013-01-30T17:15:43.567 に答える
1

最も単純な解決策は末尾再帰ではありませんが、非常に理解しやすいものです。

let prepend2 (x, y) (xs, ys) = x::xs, y::ys

let rec split = function
  | [] | [_] as xs -> xs, []
  | x0::x1::xs -> prepend2 (x0, x1) (split xs)
于 2013-01-31T17:54:36.577 に答える