我只是想知道允许函数通过参数传递的语言的优点是什么?希望有几个例子可以解决这个问题。
哪些流行的编程语言允许这样做?
答案 0 :(得分:3)
假设我有一组对象,我想根据一些标准对它们进行排序。
如果我可以将函数传递给函数,那很简单:
collection.sort((a, b) => a.SomeProperty.compareTo(b.SomeProperty));
如果我不能,那么为了做这样的事情,每次我想根据不同的标准进行排序时,我基本上必须重新实现排序算法。
答案 1 :(得分:1)
优点是,您可以将函数参数化为不易表示为普通值的函数。
例如,如果要查找满足集合中某个谓词的项,或者想要查明集合中的所有项是否满足谓词,则谓词最自然地表示为元素类型中的函数布尔。
另一个非常常见的例子是排序函数,它将比较函数作为参数,因此集合的元素可以通过自定义排序键进行排序。
当然,在不允许函数作为参数但具有对象系统(在java中读取:)的语言中,您可以通过使用实现排序函数的特定接口的对象来解决此问题。 / p>
哪些流行的编程语言允许[将函数作为参数传递]?
C,C ++,C#,Objective C,Python,Ruby,Perl,PHP,Javascript,基本上所有功能或功能语言。
应该注意的是,与列表中的其他语言不同,C和C ++(在C ++ 0x之前)没有闭包。
答案 2 :(得分:1)
能够将函数传递给其他函数的优点是它允许您编写更清晰,更通用的代码。例如,考虑一个合理实际的函数,它将列表中的所有数字相加:
sum [] = 0
sum (x:xs) = x + sum xs
这是Haskell语法,您可能不熟悉;这两行定义了两种不同的情况,具体取决于输入是否为空列表,在这种情况下总和为零,或者x
前置于列表xs
,在这种情况下总和为{ {1}}加上x
的总和。用更传统的语言(特别是任何一种语言),你都有
xs
现在,假设我们想要在列表中找到数字的乘积,而不是:
function sum(xs) {
var total = 0
for x in xs {
total = x + total
}
return total
}
在传统语言中,它看起来更像是
product [] = 1
product (x:xs) = x * product xs
有趣的是,这两个功能看起来几乎相同。唯一的区别是function product(xs) {
var total = 1
for x in xs {
total = x * total
}
return total
}
替换为0
,1
替换为+
。实际上,事实证明我们可以概括*
和sum
:
+
这里,foldr f i [] = i
foldr f i (x:xs) = f x (foldr f i xs)
有三个参数:一个双参数函数foldr
,一个常量f
和一个列表。如果列表为空,则返回常量(例如,i
);否则,我们将函数应用于(a)列表的第一个元素,以及(b)折叠列表其余部分的结果。在更传统的语言中,这看起来像
sum [] = 0
这意味着我们可以将function foldr(f,i,xs)
var result = i
for x in xs {
result = f(x, result)
}
return result
}
和sum
简化为
product
(这里,sum = foldr (+) 0
product = foldr (*) 1
和(+)
是两个参数函数,分别对它们的参数进行加法和乘法。)如果没有第一类函数,就不能这样做。 (我正在做的另一件事是取消最后一个参数;这称为currying,并且相当方便。我的想法是,如果我不给它(*)
所有的参数,它会返回一个函数,它是期待其余的人。但如果你觉得它令人困惑,想象一下定义说foldr
。)
但是这里的事情可以变得更有趣。假设您有一个数字列表,并且您想要计算列表中每个数字的平方:
sum xs = foldr (+) 0 xs
但这显然是可抽象的:如果您想要否定列表中的每个元素,或者如果您有一个电子邮件列表并想要获取发件人,或者其他任何内容,那么几乎完全相同的代码就可以工作。所以,我们抽象:
squares [] = []
squares (x:xs) = (x^2) : squares xs
function squares(xs) {
var result = []
for x in xs {
result = result ++ [x^2]
}
return result
}
但有趣的是,我们还可以根据之前的map f [] = []
map f (x:xs) = f x : map f xs
function map(f,xs) {
var result = []
for x in xs {
result = result ++ [f(x)]
}
return result
}
squares = map (^2) # (^2) is the function which squares its argument.
negations = map negate
emailSenders = map getSender
实施map
。首先,我们需要定义函数组合foldr
:
.
这表示f . g = \x -> f (g x)
和f
的组合是一个参数的新匿名函数,它本身将g
应用于g
和x
结果。现在,我们可以定义f
:
map
这里,map f = foldr ((:) . f) []
是一个获取元素和列表的函数,并返回该列表前面的元素。 (:)
与(:) . f
相同,\x -> (:) (f x)
(根据我提到的讨论规则)与\x xs -> f x : xs
相同。换句话说,在折叠的每一步,将f x
置于我们迄今为止所拥有的位置之前。 (这不会使我们与map
相反,因为foldr
“内外”工作原样。)
map
的这个定义使用高阶函数.
来构造它传递给foldr
的函数。这么多函数作为参数传递!这让我们可以做像写
f &&& g = \x -> (f x, g x)
然后用它来写
sendersAndRecipients = map (getSender &&& getRecipient) . fetchEmails
通过将函数视为值,您可以获得很多力量。您可以编写诸如map
或&&&
之类的通用过程,这些过程允许您稍后编写简洁但可读的代码。传递函数对于回调也很方便:sendMessage(theMessage,fn)
,其中fn
是收到响应时要运行的函数。这只是一种非常自然的工作方式。
至于支持这种语言的语言:老实说,维基百科知道的比我想的要好。但我会试一试。 C和C ++可以这样做:你不能在线编写函数,但你可以传递函数指针。尽管如此,它在任何一种语言中都不是很常见(在C语言中更少)。任何OO语言都可以,如果你定义一个具有你想要的功能的单一方法的类,但这非常笨重。然而,这就是Java所做的。另一方面,C#实际上具有可以作为参数传递的函数。 Ruby(也有“块”,这是某些用例的特殊语法),Python,Perl和JavaScript都支持传递函数,就像每种函数式编程语言一样:Lisps(Scheme,Common Lisp,Clojure,... ); ML家族(SML,OCaml,......); Haskell(可能与ML家族混在一起);斯卡拉;和别的。这是一个很有用的功能,所以看到它如此广泛并不奇怪。