F#模式将可变长度分隔的单词与第一个单词a命令匹配

F#模式将可变长度分隔的单词与第一个单词a命令匹配,f#,pattern-matching,F#,Pattern Matching,我只是想知道是否有更好的方法用F#编写这段代码: 我是F#的新手,我觉得这句话很重复,可以做得更好,但我真的不确定从哪里开始 基本上,它运行的是一个命令,该命令是逗号分隔字符串的第一部分,其余部分是输入: 输入示例: 选项A、1、2、A、测试 另外,.eignore只是一个用于检查字符串是否相等的自定义函数,忽略大小写。似乎重复次数最多的是将参数传递给函数。显而易见的解决方案是让他们接受字符串数组: type Command = { name : string; action : string

我只是想知道是否有更好的方法用F#编写这段代码:

我是F#的新手,我觉得这句话很重复,可以做得更好,但我真的不确定从哪里开始

基本上,它运行的是一个命令,该命令是逗号分隔字符串的第一部分,其余部分是输入: 输入示例:

选项A、1、2、A、测试


另外,
.eignore
只是一个用于检查字符串是否相等的自定义函数,忽略大小写。

似乎重复次数最多的是将参数传递给函数。显而易见的解决方案是让他们接受字符串数组:

type Command = { name : string; action : string array -> unit }
当然,您可以添加一些验证字段,如参数数等。接下来,让我们为新类型创建一些访问器:

let cmdName = function {name = n; action = _} -> n
let cmdAction = function {name = _; action = a} -> a
并实现不区分大小写的字符串比较:

let icmp a b = String.Compare(a, b, true) = 0
let (|EqIgnoreCase|_|) x y =
    if String.Equals(x, y, StringComparison.OrdinalIgnoreCase)
    then Some ()
    else None
现在,我们已经准备好实现主要功能:

type Class() as __ =
  member __.optionC(args : string array) = printfn "optionC!"
  static member optionD(args : string array) = printfn "optionD"

let run (command : string) = 
  let words = command.Split [|','|]
  [ { name = "optionA"; action = fun args -> printfn "%A" args }
    { name = "optionB"; action = fun _ -> printfn "optionB!" } 
    { name = "optionC"; action = fun args -> (new Class()).optionC(args) } 
    { name = "optionD"; action = Class.optionD } ]
  |> List.tryFind(cmdName >> (icmp words.[0]))                   // [1]
  |> Option.iter(cmdAction >> ((|>) words.[1..words.Length - 1]) // [2])
[1] 我们创建命令列表并尝试查找名为第一个参数的命令

[2] 最后,我们将所有参数传递给操作(
optionA
函数族有效地执行它)


请注意,
Option.iter
仅在找到所需命令时才会执行其有效负载。

您可以通过使用复合模式匹配来执行此操作。您需要一个不区分大小写的字符串比较:

let icmp a b = String.Compare(a, b, true) = 0
let (|EqIgnoreCase|_|) x y =
    if String.Equals(x, y, StringComparison.OrdinalIgnoreCase)
    then Some ()
    else None
您可以将此
EqIgnoreCase
模式组合成标准:

使用cons模式而不是数组模式是最容易的,因为这使您可以使用通配符模式(
\uu
)轻松忽略列表的尾部

以下是一些函数调用:

> run "optionA,1,2,A,Test";;
optionA 1 2 A
val it : unit = ()

> run "optionB,1,2,A,Test,Foo";;
optionB 1 2 A Test Foo
val it : unit = ()

> run "OPTIONA,1,2,A,Test";;
optionA 1 2 A
val it : unit = ()

> run "OPTIONA,1,2,A";;
optionA 1 2 A
val it : unit = ()

> run "OPTIONA,1,2";;
val it : unit = ()

请注意,最后一行没有控制台输出,因为参数列表太短。

什么是
optionA
optionB
,等等。?请提供a,包括输入和预期输出。上面列出的示例输入是
optionA,1,2,a,Test
optionA
optionB
。不需要有任何输出。函数只是运行一些东西。如果不需要任何输出,您可以将函数重构为:
让我们运行x=()
。这显然不是您想要的,但我的心理调试能力告诉我的仅此而已。这并不是说它不起作用。上面的方法是有效的,我只是觉得这不是做我正在做的事情的正确或最好的方法。我所要做的就是给“run”方法一个要运行的命令。示例:
选项A、1、2、A、测试
。然后,“run”方法将获取逗号分隔列表中的第一项
optionA
,并运行一个特定的方法,并将剩余的参数传递给该方法。此方法可以是任何方法,但在上面的代码中它运行
optionA
。这个例子中的参数是
1,2,A,Test
我喜欢这样。我只是想完全理解它在这个阶段是如何发展的。哈哈。有了动作,每个动作都可以有一个自定义的方法吗。示例:
{name=“optionA”;action=custom_method}
以便将参数直接传递到方法中。请参阅我的更新。此外,尽量选择函数而不是方法(OO风格)。所有函数只需要具有相同的签名(尽管可能具有不同的签名,例如,动作类型的歧视联合)。啊,好吧,太棒了。我想我现在明白了。谢谢你(堆!)这很有效。我以前不知道活动模式匹配。谢谢!:)