12

It is by now a well known theorem of the lambda calculus that any function taking two or more arguments can be written through currying as a chain of functions taking one argument:

# Pseudo-code for currying
f(x,y) -> f_curried(x)(y)

This has proven to be extremely powerful not just in studying the behavior of functions but in practical use (Haskell, etc.).

Functions returning values, however, seem to not be discussed. Programmers typically deal with their inability to return more than one value from a function by returning some meta-object (lists in R, structures in C++, etc.). It has always struck me as a bit of a kludge, but a useful one.

For instance:

# R code for "faking" multiple return values
uselessFunc <- function(dat) {
   model1 <- lm( y ~ x , data=dat )
   return( list( coef=coef(model1), form=formula(model1) ) )
}

Questions

  1. Does the lambda calculus have anything to say about a multiplicity of return values? If so, do any surprising conclusions result?
  2. Similarly, do any languages allow true multiple return values?
4

4 回答 4

5

根据关于 lambda 演算的维基百科页面:

Lambda演算,也写作λ-演算,是函数定义、函数应用和递归的形式系统

还有一个函数,在数学意义上

将一个量(函数的参数,也称为输入)与另一个量(函数的值,也称为输出)相关联

因此,回答您的第一个问题不,lambda 演算(或任何其他基于数学函数的形式)不能有多个返回值。

对于您的第二个问题,据我所知,实现多个返回值的编程语言通过将多个结果打包在某种数据结构(无论是元组、数组,甚至是堆栈)中,然后再将其解包 -这就是差异所在,因为某些编程语言使打包/解包部分对程序员透明(例如 Python 在后台使用元组),而其他语言使程序员明确地完成工作,例如 Java 程序员可以模拟多次返回通过将多个结果打包到返回的 Object 数组中,然后手动提取和转换返回的结果,可以在某种程度上获得值。

于 2011-11-22T14:16:30.450 回答
2

A function returns a single value. This is how functions are defined in mathematics. You can return multiple values by packing them into one compound value. But then it is still a single value. I'd call it a vector, because it has components. There are vector functions in mathematics there, so there are also in programming languages. The only difference is the support level from the language itself and does it facilitate it or not.

于 2012-03-14T03:35:07.763 回答
2

没有什么能阻止您拥有多个函数,每个函数都返回您想要返回的多个结果之一。

例如,假设您在 python 中有以下函数返回一个列表。

def f(x):
  L = []
  for i in range(x):
    L.append(x * i)
  return L

它返回[0, 3, 6]forx=3[0, 5, 10, 15, 20]for 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

然后,您可以请求给定输入的任何输出,并获取它,或者 get 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为 any 提供值的函数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 教程中的一个例子:

;; 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
于 2015-10-03T21:49:33.167 回答
-1

我写这篇文章是对已接受答案的迟到回应,因为它是错误的!

Lambda Calculus 确实有多个返回值,但需要花一点时间来理解返回多个值的含义。

Lambda Calculus 没有对事物集合的固有定义,但它确实允许您使用产品和教堂数字来发明它。

本示例将使用纯函数式 JavaScript。

让我们定义一个产品如下:

const product = a => b => callback => callback(a)(b);

然后我们可以定义church_0,和church_1 aka true,false,aka left,right,aka car,cdr,aka first,休息如下:

const church_0 = a => b => a;
const church_1 = a => b => b;

让我们从创建一个返回两个值 20 和“Hello”的函数开始。

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 和“Hello”。

要返回超过 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],而是您必须使用过滤掉您想要的位置的功能。

这与电路非常相似,因为电路没有“0”、“1”、“2”、“3”,但它们确实有做出决定的方法,并通过用字节抽象出我们的电路(反向列表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 个并递归地缩小您想要的特定元素。本质上,它的作用与多路复用器完全一样(除了“单个”信号实际上是唯一寻址元素所需的固定数量的位(副产品,选择))。

我的观点是,数组、映射、关联数组、列表、位、字节、字,从根本上来说都是函数,无论是在电路级别(我们可以用电线和开关来表示复杂的宇宙),还是在数学级别(所有最终是产品(序列,如果不需要嵌套就很难管理,例如列表),副产品(类型,集合)和指数(自由函子(lambdas),健忘函子))。

于 2017-02-25T06:14:06.947 回答