“一个接受整数的函数,返回一个接受整数并返回整数的函数。”
但我没有很好地理解这个问题。
有人能够清楚地解释一下吗?
[更新]:
> let f1 x y = x+y ;;
val f1 : int -> int -> int
这是什么意思?
[更新]:
> let f1 x y = x+y ;;
val f1 : int -> int -> int
这是什么意思?
让我们从头开始。
F#使用冒号(:
)符号来表示事物的类型。比如说你定义了一个int
类型的值:
let myNumber = 5
F#交互式会理解myNumber
是一个整数,并通过以下方式告诉您:
myNumber : int
该语句读作:
myNumber
的类型是int
到此为止,一切都很好。现在,让我们介绍一些新的概念,函数式类型。函数式类型简单来说就是一个函数的类型。F# 使用 ->
表示函数式类型。这个箭头符号表示左侧的内容被转化成右侧的内容。
让我们考虑一个简单的函数,它接收一个参数并将其转换为一个输出。这样一个函数的例子可以是:
isEven : int -> bool
这里介绍了函数的名称(在:
左边)和其类型。这句话可以用英语表示为:
isEven
是一个将int
转换为bool
的函数类型。
请注意,为了正确理解所说的内容,在“is of type”部分之后应短暂停顿,然后一口气读完剩下的句子,不要停顿。
在F#中,函数与普通类型相比(几乎)没有太多特殊之处。它们是可以传递给函数、从函数返回的东西,就像布尔值、整数或字符串一样。
因此,如果你有:
myNumber : int
isEven : int -> bool
你应该将int
和int -> bool
视为同一种类型的两个实体。在这里,myNumber
是int
类型的值,而isEven
是int -> bool
类型的值(当我在上面谈到短暂的停顿时,这就是我想象的东西)。
->
的类型的值也被称为函数,并具有特殊的能力:您可以将一个函数应用于一个值。例如:isEven myNumber
这意味着你正在将函数isEven
应用于值myNumber
。通过检查isEven
的类型,您可以预期它将返回一个布尔值。如果您正确实现了isEven
,那么它显然会返回false
。
让我们定义一个通用函数来确定整数是否是其他整数的倍数。我们可以想象我们函数的类型将是(括号在这里是为了帮助你理解,它们可能存在或不存在,有特殊含义):
isMultipleOf : int -> (int -> bool)
正如你可以猜测的那样,这段话的意思是:
isMultipleOf
是一种类型为(PAUSE)将int
转换为函数,函数将int
转换为bool
。
(这里的(PAUSE)表示朗读时的暂停)。
我们稍后会定义这个函数。在此之前,让我们看看如何使用它:
let isEven = isMultipleOf 2
F#交互式窗口会回答:
isEven : int -> bool
这里提到的isEven
的类型是int -> bool
,表示我们将值2 (int
) 传递给 isMultipleOf
函数后,它将返回一个新函数,即int -> bool
。
我们可以将isMultipleOf
函数视为一种创建函数的工具。
isMultipleOf
函数的定义现在,让我们来定义一下这个神秘的函数创建函数。
let isMultipleOf n x =
(x % n) = 0
很简单吧?
如果您将此输入到 F# 交互式中,它将回答:
isMultipleOf : int -> int -> bool
请注意,这里没有括号。现在对你来说并不是特别重要。只需记住箭头是右结合的。也就是说,如果你有
a -> b -> c
你应该将其解释为
a -> (b -> c)
右结合中的右表示您应该将其解释为在最右边的操作符周围有括号。因此:
a -> b -> c -> d
应该被解释为
a -> (b -> (c -> d))
isMultipleOf
的用法正如你所看到的,我们可以使用isMultipleOf
来创建新函数:
let isEven = isMultipleOf 2
let isOdd = not << isEven
let isMultipleOfThree = isMultipleOf 3
let endsWithZero = isMultipleOf 10
F# Interactive 会回应:
isEven : int -> bool
isOdd : int -> bool
isMultipleOfThree : int -> bool
endsWithZero : int -> bool
但是你可以以不同的方式使用它。如果你不想(或者不需要)创建一个新函数,你可以按照以下方式使用:
isMultipleOf 10 150
这将返回true
,因为150是10的倍数。这与创建函数endsWithZero
并将其应用于值150完全相同。(isMultipleOf 10) 150
也就是说,你要将括号放到最左边的函数应用周围。
现在,如果你能够理解所有这些,你的示例(即经典的CreateAdder
)应该很简单!
之前有人问了这个问题,它涉及到完全相同的概念,但是在Javascript中。在我的答案中,我给出了两个经典的示例(CreateAdder、CreateMultiplier),它们更加明确地返回函数。
希望这可以帮到你。
x = CreateAdder(3)
x(5) // returns 8
x(10) // returns 13
CreateAdder(20)(30) // returns 50
我对F#还不够熟悉,所以写代码时需要检查,但C#的话大概是这样的:
public static Func<int, int> CreateAdder(int amountToAdd)
{
return x => x + amountToAdd;
}
这有帮助吗?
编辑:如Bruno所指出的那样,你在问题中给出的示例正是我为C#代码提供的示例,因此上述伪代码将变为:
let x = f1 3
x 5 // Result: 8
x 10 // Result: 13
f1 20 30 // Result: 50
let x = f1 3
,x 5
,x 10
,f1 20 30
,根据你的例子。 - Bruno Reisf1
函数带一个参数的返回值只是另一个函数(在C#中委托的方式)。 - Mehrdad Afsharicreate_adder
定义为create_adder = (+)
。 - Chuck这是一个接受整数并返回接受整数并返回整数的函数。
这等效于接受两个整数并返回整数的函数。在函数式语言中,处理接受多个参数的函数的方式很常见,这使得对值进行部分应用变得容易。
例如,假设有一个add函数,它接受两个整数并将它们相加:
let add x y = x + y
您有一个列表,想要将每个项目都加上10。 您可以部分应用add
函数到值10
。 它会将其中一个参数绑定为10,并保留另一个参数未绑定。
let list = [1;2;3;4]
let listPlusTen = List.map (add 10)
map
的函数。你只需重用add
函数即可。let f x y = ...
。 - Mehrdad Afsharilet f (x,y)
接受一个单一类型的元组作为参数。 - Mehrdad Afsharipublic int Test(int takesAnInteger) { return 0; }
因此,我们得到了一个接受整数并返回(类似上面那个函数的)函数。
再看C#代码:
public int Test(int takesAnInteger) { return 0; }
public int Test2(int takesAnInteger) { return 1; }
public Func<int,int> Test(int takesAnInteger) {
if(takesAnInteger == 0) {
return Test;
} else {
return Test2;
}
}
let add x y = x + y
,它似乎添加了两个参数。但实际上,原始的add
函数只接受参数x
。当你应用它时,它会返回一个函数,该函数接受一个参数(y
),并已经填充了x
值。然后,当你应用那个函数时,它会返回所需的整数。int -> int -> int
中,这意味着它接受类型为int
的参数——一个整数,并返回类型为int -> int
的函数——一个接受整数并返回整数的函数。你会注意到,这恰好与上面介绍的柯里化函数的工作方式相匹配。这是我的两分钱。默认情况下,F#函数启用部分应用或柯里化。这意味着当您定义以下内容时:
let adder a b = a + b;;
您正在定义一个函数,该函数接受一个整数并返回一个接受整数并返回整数的函数或int -> int -> int
。然后,柯里化允许您部分应用函数以创建另一个函数:
let twoadder = adder 2;;
//val it: int -> int
上述代码将a预定义为2,因此每当您调用twoadder 3
时,它将简单地将2添加到参数。
函数参数由空格分隔的语法等同于此lambda语法:
let adder = fun a -> fun b -> a + b;;
这个概念被称为高阶函数,在函数式编程中非常常见。
函数本身只是另一种数据类型。因此,您可以编写返回其他函数的函数。当然,您仍然可以有一个以int作为参数并返回其他内容的函数。将两者结合起来,考虑以下示例(使用Python):
def mult_by(a):
def _mult_by(x):
return x*a
return mult_by
mult_by_3 = mult_by(3)
print mylt_by_3(3)
9
(很抱歉我用的是Python,但我不会F#)
这里已经有很多答案了,但是我想提供另一种看法。有时候用很多不同的方式解释同样的事情可以帮助你更好地理解。
我喜欢把函数想象成“你给我什么,我就会还给你什么”
所以一个 Func<int, string>
表示“你给我一个 int ,我就会还给你一个 string”。
我也发现,从“稍后”的角度来思考更容易: “当 你给我一个 int 的时候,我会还给你一个 string ”。当你看到像 myfunc = x => y => x + y
这样的代码的时候,这点尤其重要(“当 你给 curried 一个参数 x 时,你会得到一个东西,当 你给它一个参数 y 时,它将返回 x+y”)。
(顺便说一句,我假设你对 C# 很熟悉)
所以我们可以把你的 int -> int -> int
例子表达为 Func<int, Func<int, int>>
。
我另外一种看待 int -> int -> int
的方式是通过提供适当类型的参数逐个去掉左侧的每个元素。当你没有更多的 ->
时,你就没有剩余的“层”,并得到一个值。
static void Main()
{
//define a simple add function
Func<int, int, int> add = (a, b) => a + b;
//curry so we can apply one parameter at a time
var curried = Curry(add);
//'build' an incrementer out of our add function
var inc = curried(1); // (var inc = Curry(add)(1) works here too)
Console.WriteLine(inc(5)); // returns 6
Console.ReadKey();
}
static Func<T, Func<T, T>> Curry<T>(Func<T, T, T> f)
{
return a => b => f(a, b);
}