1

抱歉,如果我的标题描述性不够,我不知道如何表达这个问题……我习惯用 C# 编程,并且一直在涉足 F#。我正在尝试编写一个在某些 C# 脚本中使用的函数,用于检查字符串是否为数字。我有一个像这样写的 F# 函数,尽管根据 VS 它不正确,因为它期待一个 else:

let IsNumeric (entry : string) : bool =     // Function to make sure the data entered is numeric
    for c : char in entry do
        if not ((c >= '0') && (c <= '9')) then
            false
    true

如果我放入 else 语句并删除底部的 true :

let IsNumeric (entry : string) : bool =     // Function to make sure the data entered is numeric
    for c : char in entry do
        if not ((c >= '0') && (c <= '9')) then
            false
        else true

我收到这个错误

FS0001 此表达式的类型应为“bool”,但此处的类型为“unit”

...如果我像在第一个代码块中一样在底部保持 true ,我会收到关于它返回 bool 的警告,但应该忽略我不太明白的。

FS0020 此表达式的结果类型为“bool”并被隐式忽略。考虑使用'ignore' 显式丢弃该值,例如'expr |> ignore',或'let' 将结果绑定到一个名称,例如'let result = expr'。

这是我一直在尝试适应的 C# 方法:

public static bool IsNumeric(string s) //this just makes sure the string is numeric.
{
    foreach (char c in s)
    {
        if (!(c >= '0' && c <= '9') && c != '.' && c != '-')
        {
            return false;
        }

    }
    return true;
}

我应该如何处理这个?

4

4 回答 4

4

F# 不支持“提前返回” - 即您不能在中间停止函数执行。代码总是必须从头到尾完整地运行。是的,这是一个功能,而不是一个错误。它迫使您编写更清晰、更易于理解和可维护的代码。

通常在函数式编程中迭代的方式是递归:你编写一个函数来决定是继续迭代还是停止,在前一种情况下它会调用自己,而在后一种情况下它不会。

递归是迭代的最终基础,但对于大多数日常情况,标准库提供了各种各样的专用函数,这些函数本身是建立在递归或其他此类函数之上的。

例如,在这种特殊情况下,看起来您正在尝试做的是检查字符串中的所有字符以查看它们是否都是数字。看看:有一个特殊的函数可以检查序列中每个元素的谓词 - Seq.forall

let IsNumeric (entry : string) =
    entry |> Seq.forall (fun c -> (c >= '0') && (c <= '9'))
于 2021-07-07T03:13:46.637 回答
3

F# 作为大多数函数式语言不支持提前返回。最后评估的表达式是返回值。

典型的解决方案是使用递归函数,如果你做得对,它会编译成简单的循环。

let isNumeric (str: string) : bool =
    let rec loop index =
        if index >= str.Length then
            true
        else
            let c = str.[index]
            if (c >= '0' && c <= '9') || c = '.' || c = '-' then
                loop (index + 1)
            else false
    loop 0  

您可以使用 ILSpy、ILDasm 或SharpLab检查它编译的内容

大多数 F#-ish 是使用

let isNumeric str =
    String.forall (fun c -> (c >= '0' && c <= '9') || c = '.' || c = '-') str

最正确的是使用,因为您不检查正确性并且..-是正确的数字

let isNumeric (str: string) = System.Double.TryParse str |> fst
于 2021-07-07T03:16:56.440 回答
3

看起来您正在寻找.forall.exists

试试看

let isNumericChar (ch: char): bool =
    ch >= '0' && ch <= '9'

let isNumeric (entry: string): bool =
    String.forall isNumericChar entry


printfn "%A" (isNumeric "12345")
printfn "%A" (isNumeric "12a345")

您还可以在此处使用部分评估:

let isNumericChar (ch: char): bool =
    ch >= '0' && ch <= '9'

let isNumeric = String.forall isNumericChar


printfn "%A" (isNumeric "12345")
printfn "%A" (isNumeric "12a345")
于 2021-07-07T03:18:56.670 回答
0

对于惯用的 F# 代码,我建议 Fyodor Soikin 或其他用户描述的解决方案。由于 F# 函数没有提前返回,因此您要么必须使用递归,要么通常使用内置函数。在你的情况下,这Seq.forall就是你想要使用的。

但是,我仍然想添加迄今为止未提及的另一个解决方案。可能最惯用的 C# 代码转换是使用mutable变量。代码如下所示:

let isNumeric (entry : string) : bool =
    let mutable ret = true
    for c : char in entry do
        if not ((c >= '0') && (c <= '9')) then
            ret <- false
    ret

通常有些人会告诉你,它使用了一个可变的,并且没有功能。但在这种情况下,这并不重要。

虽然您应该能够理解递归,以及如何使用递归和不可变变量来解决它。这段代码仍然是惯用的,在某些情况下,更容易理解。

没有理由不在有限的范围内使用可变变量。

于 2021-07-22T00:02:13.693 回答