Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/fsharp/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么此无点F#函数的行为与非无点函数不同?_F#_Pointfree_Partial Application_Tacit Programming - Fatal编程技术网

为什么此无点F#函数的行为与非无点函数不同?

为什么此无点F#函数的行为与非无点函数不同?,f#,pointfree,partial-application,tacit-programming,F#,Pointfree,Partial Application,Tacit Programming,考虑下面的F#:- type TestClass()= 让getValFromMap m k=Map.find k m 让addToMap map ki=map |>map.add ki 让可变的someMap:Map=Map.empty 让getValFromMapPartial key=getValFromMap someMap key 让GetValFromMapPartialAndAcit=getValFromMap someMap 成员this.AddThenGet()= someMa

考虑下面的F#:-

type TestClass()=
让getValFromMap m k=Map.find k m
让addToMap map ki=map |>map.add ki
让可变的someMap:Map=Map.empty
让getValFromMapPartial key=getValFromMap someMap key
让GetValFromMapPartialAndAcit=getValFromMap someMap
成员this.AddThenGet()=
someMap int)
。然而,它们的行为非常不同,它们的编译方式也非常不同。使用dotPeek反编译,我看到
getValFromMapPartial
是一个方法,而
GetValFromMapPartialAndAcit
是一个在ctor中初始化的字段

F#不抱怨
GetValFromMapPartialAndAcit
,即使是在最高警告级别(VS 2012和2013)。但是在我上面的示例中调用这个函数失败了,可能是因为它已经包装了
someMap
的初始空版本,尽管它是可变的


为什么这两个功能之间存在差异?如果F#警告默认/无点版本可能会失败?

getValFromMapPartial
是一个真正的语法函数。它的签名是
val-getvalfrommapplical:key:string->int
。无论何时调用它,它都使用当前值
someMap
。这就是为什么它在你的例子中起作用;它访问具有条目的
someMap
版本

另一方面,
getvalfrommapartialandtacit
是一个lambda计算函数。它的签名是
val getvalfrommapartialandtacit:(string->int)
(注意括号)。lambda有一个编译器生成的闭包,其中包含计算lambda时
someMap
的版本。这就是为什么它在你的例子中不起作用;它总是访问相同的原始版本的someMap,但没有条目。

F#编译器区分函数的let绑定(有参数)和值绑定(没有参数)

  • 值定义:
    let A=…
    这样的绑定是一个值定义。在对代码下一步的任何内容进行评估之前,它的主体会被急切地评估“它在哪里”

  • 函数定义:类似于
    let f x=…
    的绑定是一个语法函数定义,在调用函数时计算其内容

由于
someMap
引用可变变量,因此在函数定义中使用此变量意味着调用函数时从变量中读取。但是,
getValFromMapPartialAndAcit
中的用法在声明时读取值

此行为不会阻止值成为函数。您可以编写
let f=fun x->…
来声明函数,而
将再次成为函数定义的一部分。但是,如果要在
=
fun
之间添加定义,则将在
f
的定义处对其进行评估,而不是在调用时


在问题的注释中,
someMap
作为可变参考单元时也会出现同样的问题。这是同样的问题。Andrew为可变参考单元重写的函数:

let getValFromMapPartialAndTacit = getValFromMap !someMap
这里,解引用操作符
(!)
在绑定值时应用,而不是在调用函数时应用。这相当于:

let mapRightNow = !someMap
let getValFromMapPartialAndTacit = getValFromMap mapRightNow

将类替换为模块,可以获得相同的行为。反编译时,生成的静态类包含
getValFromMapPartial
作为方法,以及
GetValFromMapPartialAndAcit
作为属性。也许这是一个已知的问题,但如果是这样,编译器应该发出警告吗?值得一提的是,即使变量
someMap
声明为可变,F#中的Map对象本身也是不可变的。所以
addToMap
函数返回的映射对象与作为参数传递给它的映射对象不同。@Petr-当然,所以我知道要将
addToMap
的返回值赋回到
someMap
。当然,我喜欢尽可能地避免易变性,但在我的项目(上面的repo就是从这个项目衍生出来的)中,我有一个可以添加新用户的用户地图。现在我知道要寻找什么了,我看到专家F#3.0书中提到了“真正的语法功能”。粗略的阅读表明,使用显式参数可以解决各种问题。也许为了避免各种问题,完全避免默契风格通常是一件好事…@AndrewWebb我认为签名中的括号不是合适的地方。(这部分答案可能是错误的。)重要的区别在于代码是值定义的一部分还是函数定义的一部分。我再次编辑了我的答案,试图澄清这一点。@Andrew在这个类中,我们仍然有语法函数的行为,但它是由编译器通过lambda值在内部实现的(没有在闭包中捕获someMap)。这很吸引人,而且可能令人困惑。然而,这是一个编译器内部实现细节,没有实际的结果。当我第一次看到你的例子时,我才意识到这个细节。我认为在你和马克·西格里斯特之间,你可能已经抓住了它。我会离开这里,深入思考这件事,并确保我理解它。谢谢。你确实用这个编辑澄清了一些事情。经过反思,Vandroy的回答通过区分带参数的函数的let绑定和不带参数的值的let绑定,更好地描述了这种情况。因此,我现在将此标记为答案。谢谢你们两位的回答;我对F#的理解向前迈出了重要的一步。
let mapRightNow = !someMap
let getValFromMapPartialAndTacit = getValFromMap mapRightNow