将递归函数作为成员添加到类中

hap*_*pyD 4 f# functional-programming

我在向类中添加递归函数时遇到了麻烦.

我可以使用let声明函数私有没有问题在我的成员声明之上.

但是当我尝试使用成员公开它时,它不会编译.

member this.rec mux xs ys =
  match xs with
  | [] -> ys
  | x::xt -> x :: mux ys xt
Run Code Online (Sandbox Code Playgroud)

感谢您纠正我,并指导我在线提供适当的资源.我一直在阅读很多教程,但我找不到这个信息.

Fyo*_*kin 7

成员函数总是递归的,不需要rec关键字:

member this.mux xs ys =
Run Code Online (Sandbox Code Playgroud)

(即使有一个rec关键词,它会走之前this,同样的方式private- member rec this.mux ...)

但是一旦你宣布它成为一个成员,你必须将它作为一个成员引用 - 即this.mux代替mux:

member this.mux xs ys =
  match xs with
  | [] -> ys
  | x::xt -> x :: this.mux ys xt
Run Code Online (Sandbox Code Playgroud)

为什么成员函数总是递归的,而let-bound函数则不是

(回应评论)

let绑定功能可以隐藏先前定义的标识符.例如:

let f x = x+5
let f x = x-2
let a = f 5   // a = 3, not 10
Run Code Online (Sandbox Code Playgroud)

这是完全合法的事情(模块中的顶级除外),并且经常用于实用目的,例如消毒参数:

let sendEmail email subject body =
   let email = canonicalize email
   ...
Run Code Online (Sandbox Code Playgroud)

请注意,在这个函数中,我做的第一件事是清理电子邮件,然后继续做任何事情(注意:这不是为参数"赋值"新值email,而是定义恰好具有相同值的全新值名称).

这个新定义email用于函数的其余部分而不是原始参数email.这被称为"阴影".

现在,请注意这个新定义如何email引用旧定义email.这是唯一可能的,因为email它不是递归的:编译器知道email定义中的单词是email指先前定义的值,而不是现在定义的值.

"但是等等" - 你说 - "它甚至意味着email递归?""递归"一词不适用于函数吗?" 好吧,不.值也可以是递归的,但这是另一个时间的主题.现在,这是一个不同的例子:

let notifyUsers sendEmail log =
   let sendEmail name =
      log ("Notifying " + name)
      sendEmail (name + "@contoso.com")

   sendEmail "John"
   sendEmail "Mark"
   sendEmail "Matthew"
   sendEmail "Luke"
Run Code Online (Sandbox Code Playgroud)

在这个例子中,我sendEmail用一个新的定义"遮蔽"了这个函数,该定义在调用原始文件之前记录了用户的名字sendEmail.如果我将"new"定义sendEmail为递归(即let rec sendEmail name = ...),则此程序将导致无限循环:函数将无限地调用自身.但是因为函数不是递归的,所以它能够引用先前定义的同名值.

成员函数没有这个问题:你不能遮蔽一个类方法,那将毫无意义.

在不同语言中以不同方式解决该问题.
例如,在默认情况下一切都可变的语言中,根本不会出现这个问题:你只是改变了值,就是这样......除非你想改变类型,否则你就搞砸了.
再举一个例子,在Haskell中,所有值都是递归的,而阴影会引发警告.结果,人们被迫使用刻度线或通过命名获得创造性,甚至引入不需要的monad.

  • 这是一个微妙的问题,没有任何明确的理由.简短的回答是:名字阴影.对于长期答案,我更新了原始答案. (2认同)
  • @happyD:这是F#历史包袱的一部分.它不是一种同类语言,而是一个包中的两种语言 - 一种源自OCaml(`let`,`let rec`)的高级函数语言和一种更接近本机.NET结构的低级语言( `member this.X`).这两种语言来自于很久以前在如何表达自我引用方面采取不同立场的谱系,而F#保留了这种差异而不是试图调和它. (2认同)
  • @happyD:特别是,这个答案描述了F#默认情况下let-bound函数不递归的历史背景 - http://stackoverflow.com/a/1891573/679898 (2认同)