私は適切に機能組成物を使用するのですか?
-
26-09-2019 - |
質問
。現在、いくつかのテストの後、私はこの作品を発見したが、あなたは動作します任意の言語でいくつかの恐ろしいコードを書くことができます。これは、あなたがプロのF#プログラマの書き込みを見つけるだろうか、私は機能を悪用していたコードのようなものはありますか?
注:のtest
私が言及していますどのような具体的には
type State = { input:string; index:int; succeeded:bool }
type Matcher = State -> State
let term (cs:char Set) =
fun s ->
if s.succeeded && s.index < s.input.Length && cs.Contains s.input.[s.index] then
{ input = s.input; index = s.index + 1; succeeded = true }
else
{ input = s.input; index = s.index; succeeded = false }
let quantify (term, min, max) =
let rec inner (s:State, count) =
if s.succeeded && s.index < s.input.Length && count <= max then
inner (term { input = s.input; index = s.index + 1; succeeded = true }, count + 1)
elif count >= min && count <= max then
{ input = s.input; index = s.index - 1; succeeded = true }
else
s
fun s -> inner (s, 0)
let disjunction leftTerm rightTerm =
fun s ->
let left = leftTerm s
if not left.succeeded then
let right = rightTerm s
if not right.succeeded then
{ input = s.input; index = s.index; succeeded = false }
else
right
else
left
let matcher input terms =
let r = terms { input = input; index = 0; succeeded = true }
if r.succeeded then r.input.Substring (0, r.index) else null
let test = // (abc|xyz)a{2,3}bc
disjunction // (abc|xyz)
(term (set "a") >> term (set "b") >> term (set "c"))
(term (set "x") >> term (set "y") >> term (set "z"))
>> quantify (term (set "a"), 2, 3) // (a{2,3})
>> term (set "b") // b
>> term (set "c") // c
let main () : unit =
printfn "%s" (matcher "xyzaabc" test)
System.Console.ReadKey true |> ignore
main()
解決
コードは、私にはかなり良いに見えます。
これはあなたの意図や偶然の一致だった場合、私はわからないんだけど、あなたは多くの学術論文のテーマである「パーサコンビネータ」と非常に似たものを、:-)実装しようとしています。私は単項パーサコンビネータのは非常に読みやすいです(それはの例を持っていると思いますHaskellは、しかし、あなたは)のF#にそれらを変換することができる必要があります。
関数組成オペレータについて。それは多くの場合、コードを難読化するので、私は、あまり一般的にオペレータを使用しての大ファンではないんです。あなたは簡単にその>>
手段解釈しやすい、「このグループは、そのグループに続くべきである」を想像することができるので、しかし、あなたの例では、それは良いの理にかなっています。
は、私がやるだろうと軽微な変更は、例えば、これを書くことができるように、disjunction
操作のためのいくつかの素晴らしいカスタム演算子を選択し、さらにいくつかの基本操作を定義することです
// Test against several terms in sequence
let sequence terms = (fun state -> terms |> Seq.fold (>>) state)
// Test for a substring
let substring s = sequence [ for c in s -> term (set [c]) ]
let test = // (abc|xyz)a{2,3}bc
( substring "abc" <|> substring "xyz" )
>> quantify 2 3 (term (set "a")) // (a{2,3})
>> substring "bc" // bc
それはより多くの記述(およびカプセル化>>
)ある機能を支持して>>
演算子の一部を除去するように、これは、より高いレベルの記述です。私はまた、
の代わりに(マイナーな変化である)トリプルの複数の引数を取るようにquantify
を変更します
あなたはこれをさらに一緒にプレイしたい場合は、あなたが記事を見て、あなたがparser { .. }
構文を使用できるようになるF#の計算式ビルダーを書いてみることができます。
他のヒント
これは、一般的に良いスタイルですが、あなたはいくつかのトリックを逃すと、まだ冗長性のかなりのビットを持っています。たぶんもっとこのような:
let valid (s: State) = s.succeeded && s.index < s.input.Length
...
let disjunction leftTerm rightTerm s =
let left = leftTerm s
if left.succeeded then left else
let right = rightTerm s
if right.succeeded then right else
{ s with succeeded = false }
...
let test =
let f s = set s |> term
let (++) s t = f s >> f t
disjunction ("a" ++ "b" ++ "c") ("x" ++ "y" ++ "z")
>> quantify (f "a", 2, 3)
>> "b" ++ "c"
あなたはそれがはるかに簡単にデバッグなるため閉鎖ではなく、計算を表す値を蓄積することを好むかもしれません。