12

ラムダ計算の定理としては、2つ以上の引数をとる関数を、1つの引数をとる関数のチェーンとしてカリー化することで記述できることがよく知られています。

# 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

4 に答える 4

5

ラムダ計算に関するウィキペディアのページによると:

ラムダ計算は、λ-calculusとも呼ばれ、関数の定義、関数の適用、再帰のための正式なシステムです。

そして、数学的な意味での関数:

入力とも呼ばれる関数の引数である1つの量を、出力とも呼ばれる関数の値である別の量に関連付けます。

したがって、最初の質問に答えると、ラムダ計算(または数学関数に基づく他の形式)は複数の戻り値を持つことができません。

2番目の質問については、私が知る限り、複数の戻り値を実装するプログラミング言語は、複数の結果をある種のデータ構造(タプル、配列、さらにはスタック)にパックし、後でアンパックすることによってこれを行います-違いはここにあります。一部のプログラミング言語では、パッキング/アンパック部分がプログラマーに対して透過的になります(たとえば、Pythonは内部でタプルを使用します)が、他の言語では、プログラマーが明示的にジョブを実行します。たとえば、Javaプログラマーは複数の戻り値をシミュレートできます。複数の結果を返されるObject配列にパックし、返された結果を手動で抽出してキャストすることにより、ある程度の値を取得します。

于 2011-11-22T14:16:30.450 に答える
2

関数は単一の値を返します。これは、関数が数学で定義される方法です。1つの複合値にパックすることで、複数の値を返すことができます。しかし、それでも単一の値です。コンポーネントがあるので、ベクトルと呼びます。そこには数学のベクトル関数があるので、プログラミング言語にもあります。唯一の違いは、言語自体とのサポートレベルであり、それが容易になるかどうかです。

于 2012-03-14T03:35:07.763 に答える
2

複数の関数を使用することを妨げるものはありません。各関数は、返したい複数の結果の1つを返します。

たとえば、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です。

2番目の質問に関しては、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

それが間違っているので、私はこれを受け入れられた答えへの遅い応答として書きます!

ラムダ計算には複数の戻り値がありますが、複数の値を返すことの意味を理解するには少し時間がかかります。

ラムダ計算には、コレクションの固有の定義はありませんが、製品とチャーチ数を使用してそれを発明することができます。

この例では、純粋な関数型JavaScriptが使用されます。

製品を次のように定義しましょう。

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

次に、church_0とchurch_1を定義できます。別名true、false、別名左、右、別名car、cdr、別名最初、残りは次のようになります。

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

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)("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と「ハロー」。

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入力)、word(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のみを実行し、必要な特定の要素を再帰的に絞り込みます。基本的に、マルチプレクサとまったく同じように機能します(ただし、「単一」信号は、実際には要素を一意にアドレス指定するために必要な固定数のビット(副産物、選択肢)です)。

私のポイントは、配列、マップ、連想配列、リスト、ビット、バイト、ワードはすべて、回路レベル(ワイヤーとスイッチだけで複雑な宇宙を表現できる)と数学レベル(すべてが最終的には、製品(シーケンス、ネストを必要とせずに管理するのが難しい、たとえばリスト)、副産物(タイプ、セット)、および指数(無料のファンクター(ラムダ)、忘却関手))です。

于 2017-02-25T06:14:06.947 に答える