コメントで既に述べたように、ポイントフリー スタイルで命令型コードを記述することは、まったくお勧めできません。読みやすくなるわけではないだけでなく、エラーが発生しやすくなります。これは、実行についての推論がより困難になるためです。機能的なコードであっても、ポイントフリーでないスタイルの方がはるかに読みやすい (そしてそれほど長くはない) ことがよくあります。
とにかく、あなたが尋ねたので、ここにあなたが取ることができるいくつかのステップがあります-これは実際には機能的難読化の演習であることに注意してください. あなたがやりたいことではありません。
最初の関数のコードは、次のように脱糖されます (シーケンス式が最適化されるため、効率が低下します)。
let rec toSeq (reader : SqlDataReader) toItem = Seq.delay (fun () ->
if reader.Read() then
Seq.concat [ Seq.singleton (toItem reader); toSeq reader toItem ]
else
Seq.empty)
ポイントフリー スタイルでもSeq.delay
、シーケンスを遅延実行していることを確認するために が必要です。ただし、よりポイントフリーなスタイルでコードを記述できる、わずかに異なる関数を定義できます。
// Returns a delayed sequence generated by passing inputs to 'f'
let delayArgs f args = Seq.delay (fun () -> f args)
let rec toSeq2 : (SqlDataReader * (SqlDataReader -> int)) -> seq<int> =
delayArgs (fun (reader, toItem) ->
if reader.Read() then
Seq.concat [ Seq.singleton (toItem reader); toSeq2 (reader, toItem) ]
else
Seq.empty)
さて、関数の本体は、関数に渡された単なるdelayArgs
関数です。ポイントフリー スタイルで、この関数を他の関数から構成してみることができます。ただし、これif
はトリッキーなので、3 つの関数を受け取る (そしてそれらすべてに同じ入力を渡す) コンビネータに置き換えます。
let cond c t f inp = if c inp then t inp else f inp
let rec toSeq3 : (SqlDataReader * (SqlDataReader -> int)) -> seq<int> =
delayArgs (cond (fun (reader, _) -> reader.Read())
(fun (reader, toItem) ->
Seq.concat [ Seq.singleton (toItem reader);
toSeq3 (reader, toItem) ])
(fun _ -> Seq.empty))
メンバー呼び出しをポイントフリー スタイルで扱うことはできないため、 を呼び出す関数を定義する必要がありますRead
。次に、定数関数を返す関数を使用することもできます (名前の衝突を避けるため、named konst
):
let read (reader:SqlDataReader) = reader.Read()
let konst v _ = v
この 2 つを使用すると、最後の引数と 2 番目の引数をポイントフリー スタイルに変えることができます。
let rec toSeq4 : (SqlDataReader * (SqlDataReader -> int)) -> seq<int> =
delayArgs (cond (fst >> read)
(fun (reader, toItem) ->
Seq.concat [ Seq.singleton (toItem reader);
toSeq4 (reader, toItem) ])
(konst Seq.empty))
いくつかのよりクレイジーなコンビネータを使用します ( uncurry
Haskell に存在します。list2
コンビネータはポイントフリー スタイルで記述することもできますが、アイデアは理解できると思います):
let list2 f g inp = List.Cons(f inp, List.Cons(g inp, []))
let uncurry f (a, b) = f a b
let rec toSeq5 : (SqlDataReader * (SqlDataReader -> int)) -> seq<int> =
delayArgs (cond (fst >> read)
(list2 ((uncurry (|>)) >> Seq.singleton) toSeq5 >> Seq.concat)
(konst Seq.empty))
は定義の一部として評価されるため、これは完全にはコンパイルされませんtoSeq5
が、遅延関数を挿入すると、実際には元の動作と同じ動作をする可能性があります。
要約- 上記のコードが正しいかどうか、またそれがどのように評価されるかはもうわかりません (リーダーを熱心に評価するか、他の種類のバグが含まれている可能性があります)。型チェックを行うので、おそらく動作にはそれほど遠くありません。コードは完全に判読できず、デバッグも修正も不可能です。
これは、F# で記述できるクレイジーなポイントフリー コードの極端な例と考えてください。実際には、ポイントフリー スタイルは、 を使用して関数を作成するなどの些細なことにのみ使用する必要があると思います>>
。uncurry
またはのようなコンビネータを定義する必要があるとkonst
、人々がコードを読むのは非常に難しくなります。