lambda 演算对返回值有何说明?

发布于 2024-12-17 13:48:36 字数 659 浏览 1 评论 0原文

到目前为止,lambda 演算的一个众所周知的定理是,任何采用两个或多个参数的函数都可以通过柯里化编写为采用一个参数的一系列函数:事实证明,

# 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. 同样,是否有任何语言允许真正的多个返回值?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

等待我真够勒 2024-12-24 13:48:36

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

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

以及数学意义上的函数

关联一个量,即函数的参数,也称为输入,
与另一个量,函数的值,也称为输出

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

对于你的第二个问题,据我所知,实现多个返回值的编程语言是通过将多个结果打包在某种数据结构(无论是元组、数组,甚至堆栈)中,然后稍后将其解包来实现的 -这就是差异所在,因为某些编程语言使打包/解包部分对程序员透明(例如Python在幕后使用元组),而其他语言则让程序员显式地完成工作,例如Java程序员可以模拟多个返回在一定程度上通过包装来体现价值返回的对象数组中的多个结果,然后手动提取并转换返回的结果。

According to the Wikipedia page on lambda calculus:

Lambda calculus, also written as λ-calculus, is a formal system for function
definition, function application and recursion

And a function, in the mathematical sense:

Associates one quantity, the argument of the function, also known as the input,
with another quantity, the value of the function, also known as the output

So answering your first question no, lambda calculus (or any other formalism based on mathematical functions) can not have multiple return values.

For your second question, as far as I know, programming languages that implement multiple return values do so by packing multiple results in some kind of data structure (be it a tuple, an array, or even the stack) and then unpacking it later - and that's where the differences lie, as some programming languages make the packing/unpacking part transparent for the programmer (for instance Python uses tuples under the hood) while other languages make the programmer do the job explicitly, for example Java programmers can simulate multiple return values to some extent by packing multiple results in a returned Object array and then extracting and casting the returned result by hand.

星星的軌跡 2024-12-24 13:48:36

函数返回单个值。这就是数学中函数的定义方式。您可以通过将多个值打包为一个复合值来返回它们。但它仍然是单一值。我将其称为向量,因为它有组件。数学中有向量函数,编程语言中也有向量函数。唯一的区别是语言本身的支持程度以及是否有利于它。

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.

江城子 2024-12-24 13:48:36

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

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

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

对于 x=3,它返回 [0, 3, 6];对于 ,它返回 [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 提供第 n-th 值的函数即可。

关于第二个问题,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

Nothing prevents you from having multiple functions, each one returning one of the multiple results that you would like to return.

For example, say, you had the following function in python returning a list.

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

It returns [0, 3, 6] for x=3 and [0, 5, 10, 15, 20] for x=5. Instead, you can totally have

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

Then you can request any of the outputs for a given input, and get it, or get None, if there aren't enough outputs:

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)

Computational resources may be wasted if you have to do some of the same work, as in this case. Theoretically, it can be avoided by returning another function that holds all the results inside itself.

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

So now we have

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)

Traditional untyped lambda calculus is perfectly capable of expressing this idea. (After all, it is Turing complete.) Whenever you want to return a bunch of values, just return a function that can give the n-th value for any n.

In regard to the second question, python allows for such a syntax, if you know exactly, just how many values the function is going to return.

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

Lastly, here is an example from this 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
奢华的一滴泪 2024-12-24 13:48:36

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

Lambda 演算确实有多个返回值,但需要花点时间才能理解返回多个值的含义。

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

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

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

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

然后我们可以定义church_0和church_1又名true,false,又名左,右,又名汽车,cdr,又名第一个,其余如下:

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

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

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”,但它们确实有做出决定的方法,并通过用字节(反向列表)抽象出我们的电路 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);

8 个输入),单词(16 个输入的反向列表),在这种语言中,0 作为一个字节将是 [0, 0, 0, 0 , 数字,我们可以制定一个算法,给定一个字节索引数组,以及一个表示我们想要从此数组中索引的字节,它将处理样板文件。

无论如何,我们所说的数组无非是以下内容,以更高的级别表达以表明要点:

// 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 条并递归地缩小你想要的特定元素的范围。本质上,它的作用与多路复用器完全相同(除了“单个”信号实际上是唯一寻址元素所需的固定数量的位(余积,选择))。

我的观点是,数组、映射、关联数组、列表、位、字节、字都是基本函数,无论是在电路级别(我们可以只用电线和开关来表示复杂的宇宙),还是在数学级别(其中一切最终是乘积(序列,不需要嵌套就难以管理,例如列表),余积(类型,集合)和指数(自由函子(lambda),健忘函子))。

I write this as a late response to the accepted answer since it is wrong!

Lambda Calculus does have multiple return values, but it takes a bit to understand what returning multiple values mean.

Lambda Calculus has no inherent definition of a collection of stuff, but it does allow you to invent it using products and church numerals.

pure functional JavaScript will be used for this example.

let's define a product as follows:

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

then we can define church_0, and church_1 aka true, false, aka left, right, aka car, cdr, aka first, rest as follows:

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

let's start with making a function that returns two values, 20, and "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);

As expected, we got 20 and "Hello".

To return more than 2 values, it gets a bit tricky:

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);

As you can see, a function can return an arbitrary number of return values, but to access these values, a you cannot simply use result()[0], result()[1], or result()[2], but you must use functions that filter out the position you want.

This is mindblowingly similar to electrical circuits, in that circuits have no "0", "1", "2", "3", but they do have means to make decisions, and by abstracting away our circuitry with byte(reverse list of 8 inputs), word(reverse list of 16 inputs), in this language, 0 as a byte would be [0, 0, 0, 0, 0, 0, 0, 0] which is equivalent to:

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);

After inventing a number, we can make an algorithm to, given a byte-indexed array, and a byte representing index we want from this array, it will take care of the boilerplate.

Anyway, what we call arrays is nothing more than the following, expressed in higher level to show the point:

// 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"
        ;
};

except it doesnt do 2^32 - 1 if statements, it only does 8 and recursively narrows down the specific element you want. Essentially it acts exactly like a multiplexor(except the "single" signal is actually a fixed number of bits(coproducts, choices) needed to uniquely address elements).

My point is that is Arrays, Maps, Associative Arrays, Lists, Bits, Bytes, Words, are all fundamentally functions, both at circuit level(where we can represent complex universes with nothing but wires and switches), and mathematical level(where everything is ultimately products(sequences, difficult to manage without requiring nesting, eg lists), coproducts(types, sets), and exponentials(free functors(lambdas), forgetful functors)).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文