Что говорит лямбда-исчисление о возвращаемых значениях? - PullRequest
12 голосов
/ 22 ноября 2011

К настоящему времени известной теоремой лямбда-исчисления является то, что любая функция, принимающая два или более аргумента, может быть записана посредством каррирования в виде цепочки функций, принимающих один аргумент:

# 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. Есть ли в лямбда-исчислении что-нибудь, что можно сказать о множественности возвращаемых значений?Если так, какие-нибудь удивительные выводы приводят?
  2. Аналогично, допускают ли какие-либо языки истинные множественные возвращаемые значения?

Ответы [ 4 ]

5 голосов
/ 22 ноября 2011

Согласно Википедии страница по лямбда-исчислению:

Лямбда-исчисление, также записанное как λ-исчисление, является формальной системой для функции определение, применение функции и рекурсия

И функция, в математическом смысле :

Связывает одно значение, аргумент функции, также известный как вход, с другой величиной, значением функции, также известным как вывод

Поэтому, отвечая на ваш первый вопрос «нет», лямбда-исчисление (или любой другой формализм, основанный на математических функциях) не может иметь несколько возвращаемых значений.

На ваш второй вопрос, насколько мне известно, языки программирования, которые реализуют множественные возвращаемые значения, делают это путем упаковки нескольких результатов в некую структуру данных (будь то кортеж, массив или даже стек), а затем распаковывают это позже - и в этом заключаются различия, поскольку некоторые языки программирования делают прозрачной упаковку / распаковку для программиста (например, Python использует кортежи внутри), в то время как другие языки заставляют программиста выполнять работу явно, например, программисты на Java могут Имитировать несколько возвращаемых значений в некоторой степени, упаковав несколько результатов в возвращенный массив Object, а затем извлекать и приводить полученный результат вручную.

2 голосов
/ 04 октября 2015

Ничто не мешает вам иметь несколько функций, каждая из которых возвращает один из нескольких результатов, которые вы хотели бы вернуть.

Например, скажем, у вас в 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)

Традиционное нетипизированное лямбда-исчисление вполне способно выразить эту идею. (В конце концов, все по Тьюрингу завершено.) Всякий раз, когда вы хотите вернуть набор значений, просто верните функцию, которая может дать значение 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-урока :

;; 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
2 голосов
/ 14 марта 2012

Функция возвращает одно значение.Так определяются функции в математике.Вы можете вернуть несколько значений, упаковав их в одно составное значение.Но тогда это все-таки единая ценность.Я бы назвал это вектором, потому что в нем есть компоненты.Там есть векторные функции в математике, так же как и в языках программирования.Единственное отличие - это уровень поддержки от самого языка, облегчает он его или нет.

0 голосов
/ 25 февраля 2017

Я пишу это как поздний ответ на принятый ответ, так как он неправильный!

Лямбда-исчисление имеет несколько возвращаемых значений, но требуется немного времени, чтобы понять, что означает возвращение нескольких значений.

Лямбда-исчисление не имеет внутреннего определения набора вещей, но оно позволяет вам изобретать его, используя продукты и церковные цифры.

Чистый функциональный JavaScript будет использоваться для этого примера.

давайте определим продукт следующим образом:

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

тогда мы можем определить church_0 и church_1, иначе true, false, иначе слева, справа, иначе car, cdr, иначе 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».

Toвернуть более 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);

Как видите, функция может возвращать произвольное количество возвращаемых значений, но для доступа к этим значениям вы не можете просто использовать результат () [0], результат () [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);

После изобретения числа мы можем сделать алгоритм, учитываяИндекс байтового массива и байт, представляющий индекс, который мы хотим получить из этого массива, позаботятся о шаблоне.

В любом случае, то, что мы называем массивами, - не что иное, как следующее, выраженное на более высоком уровне, чтобы показать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"
        ;
};

за исключением того, что он не выполняет 2 ^ 32 - 1 if операторов, он делает только 8 и рекурсивно сужает конкретный элемент, который вы хотите.По сути, он действует точно так же, как мультиплексор (за исключением того, что «одиночный» сигнал на самом деле является фиксированным числом битов (копроизведений, вариантов выбора), необходимых для уникальной адресации элементов).

Моя точка зрения заключается в том, что массивы, карты, ассоциативные массивы, списки, биты, байты, слова - это все основные функции, как на уровне цепей (где мы можем представлять сложные вселенные только с помощью проводов и переключателей),и математический уровень (где все в конечном счете является продуктами (последовательностями, сложными в управлении без необходимости вложения, например списками), копроизведениями (типами, наборами) и экспонентами (свободные функторы (лямбды), функторы забвения)).

...