lambda演算对返回值有什么看法?

时间:2011-11-22 13:49:55

标签: r return-value lambda-calculus

现在是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) ) )
}

问题

  1. lambda演算有多少关于多重返回值的说法吗?如果是这样,会得出任何令人惊讶的结论吗?
  2. 同样,任何语言都允许真正的多个返回值吗?

4 个答案:

答案 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),健忘的算子))。