现在是lambda演算的一个众所周知的定理,任何带有两个或多个参数的函数都可以通过currying作为一个函数链来写一个参数:
# Pseudo-code for currying
f(x,y) -> f_curried(x)(y)
事实证明,这不仅在研究函数的行为方面非常强大,而且在实际应用中也是如此(Haskell等)。
然而,似乎没有讨论返回值的函数。程序员通常通过返回一些元对象(R中的列表,C ++中的结构等)来处理它们无法从函数返回多个值。它总是让我感觉像是一个有用的,但是很有用。
例如:
# R code for "faking" multiple return values
uselessFunc <- function(dat) {
model1 <- lm( y ~ x , data=dat )
return( list( coef=coef(model1), form=formula(model1) ) )
}
问题
答案 0 :(得分:5)
根据关于lambda演算的维基百科page:
Lambda演算,也写为λ演算,是一个正式的函数系统 定义,函数应用和递归
一个函数,在数学sense中:
关联一个数量,函数的参数,也称为输入, 与另一个数量,函数的值,也称为输出
所以回答你的第一个问题不,lambda演算(或任何其他基于数学函数的形式)不能有多个返回值。
关于你的第二个问题,据我所知,实现多个返回值的编程语言是通过将多个结果打包到某种数据结构(无论是元组,数组,甚至是堆栈)然后解包来实现的。稍后 - 这就是差异所在,因为一些编程语言使得打包/解包部分对程序员来说是透明的(例如Python在引擎盖下使用元组),而其他语言使程序员明确地完成工作,例如Java程序员可以通过在返回的Object数组中打包多个结果,然后手动提取并转换返回的结果,在某种程度上模拟多个返回值。
答案 1 :(得分:2)
函数返回单个值。这就是数学中函数的定义方式。您可以通过将多个值打包为一个复合值来返回多个值。但那时它仍然是一个单一的价值。我称之为矢量,因为它有组件。在那里有数学中的向量函数,所以在编程语言中也有。唯一的区别是语言本身的支持水平,是否有利于它。
答案 2 :(得分:2)
没有什么能阻止你拥有多个功能,每个功能都会返回你想要返回的多个结果中的一个。
例如,比方说,你在python中返回一个列表时有以下函数。
def f(x):
L = []
for i in range(x):
L.append(x * i)
return L
它为[0, 3, 6]
返回x=3
,为[0, 5, 10, 15, 20]
返回x=5
。相反,你可以完全拥有
def f_nth_value(x, n):
L = []
for i in range(x):
L.append(x * i)
if n < len(L):
return L[n]
return None
然后,如果输出不足,您可以请求给定输入的任何输出,并获取它,或获取None
:
In [11]: f_nth_value(3, 0)
Out[11]: 0
In [12]: f_nth_value(3, 1)
Out[12]: 3
In [13]: f_nth_value(3, 2)
Out[13]: 6
In [14]: f_nth_value(3, 3)
In [15]: f_nth_value(5, 2)
Out[15]: 10
In [16]: f_nth_value(5, 5)
如果您必须执行某些相同的工作,可能会浪费计算资源,就像在这种情况下一样。从理论上讲,可以通过返回另一个保存所有结果的函数来避免它。
def f_return_function(x):
L = []
for i in range(x):
L.append(x * i)
holder = lambda n: L[n] if n < len(L) else None
return holder
现在我们有了
In [26]: result = f_return_function(5)
In [27]: result(3)
Out[27]: 15
In [28]: result(4)
Out[28]: 20
In [29]: result(5)
传统的无类型lambda演算完全能够表达这个想法。 (毕竟,它是图灵完成的。)每当你想要返回一堆值时,只需返回一个可以为任何n-th
提供n
值的函数。
关于第二个问题,python允许这样的语法,如果你确切知道,函数将返回多少个值。
def f(x):
L = []
for i in range(x):
L.append(x * i)
return L
In [39]: a, b, c = f(3)
In [40]: a
Out[40]: 0
In [41]: b
Out[41]: 3
In [42]: c
Out[42]: 6
In [43]: a, b, c = f(2)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-43-5480fa44be36> in <module>()
----> 1 a, b, c = f(2)
ValueError: need more than 2 values to unpack
In [44]: a, b, c = f(4)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-44-d2c7a6593838> in <module>()
----> 1 a, b, c = f(4)
ValueError: too many values to unpack
最后,这是Lisp tutorial的一个例子:
;; in this function, the return result of (+ x x) is not assigned so it is essentially
;; lost; the function body moves on to the next form, (* x x), which is the last form
;; of this function body. So the function call only returns (* 10 10) => 100
* ((lambda (x) (+ x x) (* x x)) 10)
=> 100
;; in this function, we capture the return values of both (+ x x) and (* x x), as the
;; lexical variables SUM and PRODUCT; using VALUES, we can return multiple values from
;; a form instead of just one
* ((lambda (x) (let ((sum (+ x x)) (product (* x x))) (values sum product))) 10)
=> 20 100
答案 3 :(得分:-1)
我认为这是对已接受答案的迟回应,因为这是错误的!
Lambda Calculus确实有多个返回值,但需要一点点才能理解返回多个值的含义。
Lambda Calculus没有固有的东西集合定义,但它确实允许你用产品和教堂数字发明它。
纯函数JavaScript将用于此示例。
让我们按如下方式定义产品:
const product = a => b => callback => callback(a)(b);
然后我们可以定义church_0,而church_1又名true,false,又名left,right,aka car,cdr,aka first,rest如下:
const church_0 = a => b => a;
const church_1 = a => b => b;
让我们开始创建一个返回两个值的函数,20和&#34; Hello&#34;。
const product = a => b => callback => callback(a)(b);
const church_0 = a => b => a;
const church_1 = a => b => b;
const returns_many = () => product(20)("Hello");
const at_index_zero = returns_many()(church_0);
const at_index_one = returns_many()(church_1);
console.log(at_index_zero);
console.log(at_index_one);
正如预期的那样,我们得到了20&#34; Hello&#34;。
要返回2个以上的值,会有点棘手:
const product = a => b => callback => callback(a)(b);
const church_0 = a => b => a;
const church_1 = a => b => b;
const returns_many = () => product(20)(
product("Hello")(
product("Yes")("No")
)
);
const at_index_zero = returns_many()(church_0);
const at_index_one = returns_many()(church_1)(church_0);
const at_index_two = returns_many()(church_1)(church_1)(church_0);
console.log(at_index_zero);
console.log(at_index_one);
console.log(at_index_two);
正如您所看到的,函数可以返回任意数量的返回值,但是要访问这些值,您不能简单地使用result()[0],result()[1]或result()[2 ],但你必须使用过滤掉你想要的位置的函数。
这与电路类似,因为电路没有&#34; 0&#34;,&#34; 1&#34;,&#34; 2&#34;,&#34; 3&#34但是,他们确实有办法做出决定,并通过字节(8个输入的反向列表),字(16个输入的反向列表)抽象出我们的电路,在这种语言中,0作为一个字节将是[0,0 ,0,0,0,0,0,0],相当于:
const Byte = a => b => c => d => e => f => g => h => callback =>
callback(a)(b)(c)(d)(e)(f)(g)(h);
const Byte_one = Byte(0)(0)(0)(0)(0)(0)(0)(1); // preserves
const Bit_zero = Byte_one(b7 => b6 => b5 => b4 => b3 => b2 => b1 => b0 => b0);
在发明一个数字之后,我们可以制作一个算法,给定一个字节索引的数组,以及一个表示我们希望从这个数组得到索引的字节,它将处理样板。
无论如何,我们所谓的数组只不过是以下内容,用更高级别表示来表明这一点:
// represent nested list of bits(addresses)
// to nested list of bits(bytes) interpreted as strings.
const MyArray = function(index) {
return (index == 0)
? "0th"
: (index == 1)
? "first"
: "second"
;
};
除了它没有做2 ^ 32 - 1 if语句,它只做8并递归地缩小你想要的特定元素。本质上它的作用与多路复用器完全相同(&#34;单个&#34;信号实际上是唯一地址元素所需的固定位数(副产品,选项))。
我的观点是阵列,地图,关联阵列,列表,位,字节,字,都是基本功能,在电路层面(我们可以用线和开关代表复杂的宇宙)和数学水平(一切都是最终的产品(序列,难以管理而不需要嵌套,例如列表),副产品(类型,集合)和指数(自由函子(lambdas),健忘的算子))。