gpt4 book ai didi

F#:配管 vs. 作曲 vs. ......作曲?

转载 作者:行者123 更新时间:2023-12-03 23:34:59 25 4
gpt4 key购买 nike

我对一切都很陌生——F#、一般的编程和这个社区。我是一名数学家,在本科期间短暂接触过计算机科学。我正在尝试在 F# 和 "F# Cheat Sheet" 中完成一些任务在不解释重复的情况下展示了看似三种不同的函数组合方式。这是链接中的相关信息,以了解我的意思。

let 关键字还定义了命名函数。

let negate x = x * -1 
let square x = x * x
let print x = printfn "The number is: %d" x

let squareNegateThenPrint x =
print (negate (square x))

管道运算符(operator) |> 用于将函数和参数链接在一起。双反引号标识符可以方便地提高可读性,尤其是在单元测试中:
let ``square, negate, then print`` x = 
x |> square |> negate |> print

合成运算符 >> 用于组合函数:
let squareNegateThenPrint' = 
square >> negate >> print

通过检查并在 VS F# 中与功能交互:
  • squareNegateThenPrint x
  • ``平方,取反,然后打印'' x
  • squareNegateThenPrint'

  • it appears that this is a list of 3 ways to accomplish the exact same thing, are there any nuances here? I am convinced that given the same int they will all return the same int, but how about beyond that? What am I not seeing? What are advantages and disadvantages of each of the three methods?



    2 和 3 都使用“运算符”,而 1 似乎是组合函数以从旧函数创建新函数的常用“数学”方式。我怀疑选项 3 真的等同于 1(从某种意义上说, >> 运算符被定义为使得 square >> negate >> print 实际上被计算为 print (negate (square x)) 但代码的可读性有利于您按照它们的顺序查看函数名称发生而不是使用通常的数学符号的相反顺序,并且定义这种方式可以节省一两次击键,因为您不必在函数名称的末尾包含 x 因为 >> 运算符可能使左侧函数自动继承对右边函数变量的依赖,无需显式引用变量。

    But then how does the piping method play into this? Is the piping operator a more general operator that just so happens to work for function composition?



    另外,我在谷歌上做了很多,并尝试在发布之前阅读文档,但我没有得到任何结果。我敢肯定,如果我继续学习这门语言,明年的某个时候我会理解其中的差异。但我也相信这里有人可以加快这个过程并解释或提供一些很好的例子。最后,我不精通 C# 或任何其他语言(数学除外),因此对一个完全的菜鸟而不是一个 f# 菜鸟的解释表示赞赏。谢谢!

    最佳答案

    首先 - 是的,所有这些方式在“逻辑上”和编译到硬件时都是等效的。这是因为 |>>>运算符定义为 inline .定义大致如下:

    let inline (|>) x f = f x
    let inline (>>) f g = fun x -> g (f x)
    inline 的含义关键字是编译器会将对函数的调用替换为函数体,然后编译结果。因此,以下两种情况:
    x |> f |> g
    (f >> g) x

    将按照与以下内容完全相同的方式进行编译:
    g (f x)

    然而,在实践中,存在一些陷阱。

    一个问题是类型推断及其与类/接口(interface)的相互作用。考虑以下:
    let b = "abcd" |> (fun x -> x.Length)
    let a = (fun x -> x.Length) "abcd"

    即使这些定义在逻辑上和编译形式上都是等价的,但第一个定义将编译,而第二个定义不会。发生这种情况是因为 F# 中的类型推断是从左到右进行的,没有双反,因此,在第一个定义中,当编译器到达 x.Length 时,它已经知道 xstring ,因此它可以正确解析成员查找。在第二个示例中,编译器不知道 x是,因为它没有遇到参数 "abcd"然而。

    另一个问题与可怕的 Value Restriction 有关.简单来说,它说在语法上(不是逻辑上!)一个值(而不是一个函数)的定义不能是通用的。这与可变性有关的原因不明 - 请参阅链接文章以获得解释。

    将此应用于函数组合,请考虑以下代码(注意 fg 都是通用函数):
    let f x = [x]
    let g y = [y]

    let h1 = f >> g
    let h2 x = x |> f |> g

    在这里, h2可以正常编译,但是 h1不会,提示值(value)限制。

    在实践中 ,这三种方式之间的选择通常归结为可读性和便利性。这些都不是天生就比其他更好。在编写代码时,我通常会根据自己的喜好进行选择。

    关于F#:配管 vs. 作曲 vs. ......作曲?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51640498/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com