再帰は、関数型言語でループを記述するための基本的なメカニズムであるため、(サンプルで行っているように) 文字を反復処理する必要がある場合は、再帰が必要です。
コードを改善したい場合は、line.[2..]
非効率になるため、使用を避ける必要があります (文字列はこの種の処理用に設計されていません)。文字列をリストに変換してから処理することをお勧めします。
let convert (line:string) =
let rec loop acc line =
match line with
| ' '::' '::rest -> loop (acc + 1) rest
| _ -> (acc, line)
loop 0 (List.ofSeq line)
標準ライブラリのさまざまな関数を使用して、これをより短い方法で実装できますが、通常は再帰的でもあります (再帰が表示されないだけです! Seq.unfold
) Seq.fold
。コードよりも複雑です)。
標準ライブラリを使用したより簡潔なアプローチは、TrimLeft
メソッド (コメントを参照) を使用するか、標準の F# ライブラリ関数を使用して、次のようにすることです。
let convert (line:string) =
// Count the number of spaces at the beginning
let spaces = line |> Seq.takeWhile (fun c -> c = ' ') |> Seq.length
// Divide by two - we want to count & skip two-spaces only
let count = spaces / 2
// Get substring starting after all removed two-spaces
count, line.[(count * 2) ..]
編集文字列とリスト処理のパフォーマンスに関して、問題は、リストをスライスすると参照が変更されるだけで、スライスすると新しい文字列が割り当てられることです (これは、.NET プラットフォームでの文字列の表現方法であるためです)。簡単なテストを次に示します。
let rec countList n s =
match s with
| x::xs -> countList (n + 1) xs
| _ -> n
let rec countString n (s:string) =
if s.Length = 0 then n
else countString (n + 1) (s.[1 ..])
let l = [ for i in 1 .. 10000 -> 'x' ]
let s = new System.String('x', 10000)
#time
for i in 0 .. 100 do countList 0 l |> ignore // 0.002 sec (on my machine)
for i in 0 .. 100 do countString 0 s |> ignore // 5.720 sec (on my machine)