見出し画像

coconut チュートリアル

markdown


# Coconut Tutorial



# ココナッツチュートリアル

```{contents}

---

local:

---

```


## はじめに


[Coconut プログラミング言語](http://evhub.github.io/coconut/) のチュートリアルへようこそ! Coconut は、**シンプルでエレガントな Python 関数型プログラミング** 用に構築された [Python](https://www.python.org/) のバリアントです。しかし、これは単なる言葉です。実際には、_有効な Python 3 はすべて有効な Coconut_ ですが、Coconut は Python 上に _関数型プログラミング用のシンプルでエレガントなユーティリティ_ スイートを構築します。


Coconut を使用する理由 Coconut は便利になるように構築されています。Coconut は、Python プログラマーのレパートリーを拡張して、最新の関数型プログラミングのツールを含めます。そのため、これらのツールは _使いやすく_、非常に _強力_ です。つまり、Coconut は Python が命令型プログラミングに対して行ったことと同じことを関数型プログラミングに対して行います。また、Coconut コードは _どの Python バージョン_ でも同じように実行されるため、Python 2/3 の分割は過去のものとなります。

具体的には、Coconut は Python に次の _組み込みの構文サポート_ を追加します:


- パターン マッチング


- 代数データ型


- 構造化代入


- 部分適用


- 遅延リスト

- 関数合成


- よりきれいなラムダ


- 中置記法


- パイプライン スタイルのプログラミング


- 演算子関数


- 末尾呼び出しの最適化

- where ステートメント


その他多数!


#

## インタラクティブ チュートリアル


このチュートリアルは非インタラクティブです。代わりにインタラクティブ チュートリアルを入手するには、[Coconut のインタラクティブ チュートリアル](https://hmcfabfive.github.io/coconut-tutorial) をご覧ください。


ただし、インタラクティブ チュートリアルはこのチュートリアルほど最新ではないため、古い非推奨の構文 (ただし、そのような状況に遭遇した場合は Coconut が通知します) や時代遅れの慣用句 (つまり、インタラクティブ チュートリアルのサンプル コードは、ここでのサンプル コードよりも洗練されていない可能性があります) が含まれている可能性があります。

## Installation


At its very core, Coconut is a compiler that turns Coconut code into Python code. That means that anywhere where you can use a Python script, you can also use a compiled Coconut script. To access that core compiler, Coconut comes with a command-line utility, which can

- compile single Coconut files or entire Coconut projects,

- interpret Coconut code on-the-fly, and

- hook into existing Python applications like IPython/Jupyter and MyPy.


Installing Coconut, including all the features above, is drop-dead simple. Just


1. install [Python](https://www.python.org/downloads/),

2. open a command-line prompt,

3. and enter:


## インストール


Coconut は、本質的には Coconut コードを Python コードに変換するコンパイラです。つまり、Python スクリプトを使用できる場所であればどこでも、コンパイルされた Coconut スクリプトも使用できます。Coconut には、コア コンパイラにアクセスするためのコマンド ライン ユーティリティが付属しており、次の操作を実行できます。

- 単一の Coconut ファイルまたは Coconut プロジェクト全体をコンパイルする

- Coconut コードをオンザフライで解釈する

- IPython/Jupyter や MyPy などの既存の Python アプリケーションにフックする


上記のすべての機能を含む Coconut のインストールは、非常に簡単です。


1. [Python](https://www.python.org/downloads/) をインストールする


2. コマンド ライン プロンプトを開く


3. 次のように入力するだけです。

```

pip install coconut

```



_Note: If you are having trouble installing Coconut, try following the debugging steps in the [installation section of Coconut's documentation](./DOCS.md#installation)._


To check that your installation is functioning properly, try entering into the command line


_注: Coconut のインストールで問題がある場合は、[Coconut のドキュメントのインストール セクション](./DOCS.md#installation) のデバッグ手順に従ってください。_


インストールが正常に機能しているかどうかを確認するには、コマンド ラインに次のように入力してください。

```

coconut -h

```

Coconut のコマンドライン ヘルプが表示されます。


_注: 問題がある場合、またはこのチュートリアルに記載されている内容がうまくいかない場合は、[Gitter でヘルプを依頼](https://gitter.im/evhub/coconut) してください。できるだけ早く誰かが質問に答えます。_

## はじめに


#

## Using the Interpreter


Now that you've got Coconut installed, the obvious first thing to do is to play around with it. To launch the Coconut interpreter, just go to the command line and type


## インタプリタの使用


Coconut をインストールしたら、まずは試してみるのが当然です。Coconut インタプリタを起動するには、コマンド ラインで次のように入力します。

```

coconut

```


and you should see something like


そして次のようなものが表示されるはずです

```coconut

Coconut Interpreter vX.X.X:

(enter 'exit()' or press Ctrl-D to end)

>>>

```


which is Coconut's way of telling you you're ready to start entering code for it to evaluate. So let's do that!


In case you missed it earlier, _all valid Python 3 is valid Coconut_. That doesn't mean compiled Coconut will only run on Python 3—in fact, compiled Coconut will run the same on any Python version—but it does mean that only Python 3 code is guaranteed to compile as Coconut code.


That means that if you're familiar with Python, you're already familiar with a good deal of Coconut's core syntax and Coconut's entire standard library. To show that, let's try entering some basic Python into the Coconut interpreter. For example:


これは、Coconut が評価するコードを入力する準備ができたことを知らせる方法です。では、入力してみましょう!


先ほど見逃した方のために言っておきますが、_有効な Python 3 はすべて有効な Coconut_ です。これは、コンパイルされた Coconut が Python 3 でのみ実行されるという意味ではありません。実際、コンパイルされた Coconut はどのバージョンの Python でも同じように実行されますが、Python 3 コードのみが Coconut コードとしてコンパイルされることが保証されていることを意味します。


つまり、Python に精通している場合は、Coconut のコア構文の多くと Coconut の標準ライブラリ全体にすでに精通していることになります。それを示すために、Coconut インタープリターに基本的な Python を入力してみましょう。例:

```coconut_pycon

>>> "hello, world!"

'hello, world!'

>>> 1 + 1

2

```

## Writing Coconut Files


Of course, while being able to interpret Coconut code on-the-fly is a great thing, it wouldn't be very useful without the ability to write and compile larger programs. To that end, it's time to write our first Coconut program: "hello, world!" Coconut-style.


First, we're going to need to create a file to put our code into. The file extension for Coconut source files is `.coco`, so let's create the new file `hello_world.coco`. After you do that, you should take the time now to set up your text editor to properly highlight Coconut code. For instructions on how to do that, see the documentation on [Coconut syntax highlighting](./DOCS.md#syntax-highlighting).


Now let's put some code in our `hello_world.coco` file. Unlike in Python, where headers like


## Coconut ファイルの記述


もちろん、Coconut コードをその場で解釈できるのは素晴らしいことですが、より大きなプログラムを記述してコンパイルする機能がなければ、あまり役に立ちません。そのために、最初の Coconut プログラム「hello, world!」を Coconut スタイルで記述します。


まず、コードを入れるファイルを作成する必要があります。Coconut ソース ファイルのファイル拡張子は

`.coco`

では、新しいファイルを作成しましょう

`hello_world.coco`

. それが終わったら、テキスト エディターを設定して、Coconut コードを適切に強調表示する必要があります。その方法については、[Coconut 構文の強調表示](./DOCS.md#syntax-highlighting) のドキュメントを参照してください。


では、

`hello_world.coco`

ファイルです。Pythonとは異なり、

```coconut_python

#!/usr/bin/env python

# -*- coding: UTF-8 -*-

from __future__ import print_function, absolute_import, unicode_literals, division

```


are common and often very necessary, the Coconut compiler will automatically take care of all of that for you, so all you need to worry about is your own code. To that end, let's add the code for our "hello, world!" program.


In pure Python 3, "hello, world!" is


は一般的で、多くの場合非常に必要になりますが、Coconut コンパイラは自動的にそれらすべてを処理するので、心配する必要があるのは自分のコードだけです。そのために、「hello, world!」プログラムのコードを追加しましょう。


純粋な Python 3 では、「hello, world!」は

```coconut_python

print("hello, world!")

```


and while that will work in Coconut, equally as valid is to use a pipeline-style approach, which is what we'll do, and write


これはCoconutでも機能しますが、パイプラインスタイルのアプローチを使用することも同様に有効です。これは私たちが行うことです。

```coconut

"hello, world!" |> print

```

ココナッツの

`|>`

演算子を使用すると、パイプライン形式のプログラミングが可能になります。つまり、オブジェクトを関数から関数へと渡し、各ステップで異なる操作を実行できるようになります。この場合、オブジェクトをパイプしています

`"hello, world!"`

作戦に

`print`

では、簡単な「hello, world!」プログラムを保存して実行してみましょう。


#

## Using the Compiler


Compiling Coconut files and projects with the Coconut command-line utility is incredibly simple. Just `cd` into the directory of your `hello_world.coco` file and type


## コンパイラの使用


Coconut コマンドライン ユーティリティを使用して Coconut ファイルとプロジェクトをコンパイルするのは非常に簡単です。

`cd`

あなたのディレクトリに

`hello_world.coco`

ファイルとタイプ

```

coconut hello_world.coco

```


which should give the output


出力は次のようになる

```

Coconut: Compiling       hello_world.coco ...

Coconut: Compiled to     hello_world.py .

```


and deposit a new `hello_world.py` file in the same directory as the `hello_world.coco` file. You should then be able to run that file with


そして新しい

`hello_world.py`

ファイルと同じディレクトリに

`hello_world.coco`

ファイル。その後、そのファイルを次のように実行できるはずです。

```

python hello_world.py

```

それによって

`hello, world!`

出力として。_注: Coconutの

`--run`

オプション (

`-r`

略して「」。_


ただし、Coconut コマンドライン ユーティリティを使用する方法は、単一のファイルをコンパイルするだけではありません。また、最初の引数としてそのディレクトリを渡すだけで、特定のディレクトリ内のすべてのCoconutファイルをコンパイルすることもできます。これにより、各ファイルで同じCoconutヘッダーコードを実行する必要がなくなり、

`__coconut__.py`

同じディレクトリ内のファイル。Coconutコンパイラはさまざまなコンパイルオプションをサポートしており、そのヘルプには次のように入力することでいつでもアクセスできます。

`coconut -h`

コマンドラインに入力します。最も役に立つものの一つは

`--line-numbers`

(または

`-l`

略して「」。使用

`--line-numbers`

は、ソース コードの行番号をコンパイルされたコードにコメントとして追加します。これにより、ソース コードのどの行がエラーが発生したコンパイルされたコードの行に対応しているかがわかり、デバッグが容易になります。_注: Coconut コンパイラーを完全に制御する必要がない場合は、[インポートするだけで Coconut コードにアクセス]( することもできます。/ドキュメント。md#automatic-compilation) は、Coconut インタープリタから、または [

`coconut.api`

](./DOCS.md#coconut-api)._ #

## Using IPython/Jupyter


Although all different types of programming can benefit from using more functional techniques, scientific computing, perhaps more than any other field, lends itself very well to functional programming, an observation the case studies in this tutorial are very good examples of. That's why Coconut aims to provide extensive support for the established tools of scientific computing in Python.


To that end, Coconut provides [built-in IPython/Jupyter support](./DOCS.md#ipython-jupyter-support). To launch a Jupyter notebook with Coconut, just enter the command


## IPython/Jupyter の使用


あらゆる種類のプログラミングは、より関数的な手法を使用することで恩恵を受けることができますが、科学計算はおそらく他のどの分野よりも関数型プログラミングに非常に適しています。このチュートリアルのケース スタディは、その良い例です。そのため、Coconut は Python の科学計算の確立されたツールを広範囲にサポートすることを目指しています。


そのために、Coconut は [組み込みの IPython/Jupyter サポート](./DOCS.md#ipython-jupyter-support) を提供します。Coconut で Jupyter ノートブックを起動するには、次のコマンドを入力します。

```

coconut --jupyter notebook

```

_あるいは、CoconutをカーネルとしてJupyterインタープリターを起動するには、以下を実行します。

`coconut --jupyter console`

その代わり。さらに、現在の名前空間から初期化された対話型Coconut Jupyterコンソールを起動するには、次のように入力します。

`from coconut import embed; embed()`

コードに組み込むと、非常に便利なデバッグ ツールになります。_


#

## ケース スタディ


Coconut は便利に使えるように作られているため、デモを行うには実際に動作させてみるのが最善の方法です。そのため、このチュートリアルの大部分では、Coconut を適用して特定の問題を解決する方法を説明します。これをケース スタディと呼びます。


これらのケース スタディは、Coconut のすべての機能の完全な概要を示すことを目的としたものではありません。そのためには、Coconut の [ドキュメント](./DOCS.md) を参照してください。代わりに、Coconut を実際に使用して実用的なプログラミング問題を解決する方法を示すことを目的としています。

## ケーススタディ 1:

`factorial`

最初のケーススタディでは、

`factorial`

関数、つまり計算する関数

`n!`

どこ

`n`

整数です

`>= 0`

。これは Python でかなり簡単に実行できるため、ややおもちゃの例ですが、Coconut の基本的な機能のいくつかと、それらを効果的に使用する方法を示す良い例となります。まず、どのような実装にするかを決めなければなりません

`factorial`

私たちが望むこと。この問題に取り組む方法は様々ですが、簡潔にするために、命令型、再帰型、反復型、および

`addpattern`

.


#

## Imperative Method


The imperative approach is the way you'd write `factorial` in a language like C. Imperative approaches involve lots of state change, where variables are regularly modified and loops are liberally used. In Coconut, the imperative approach to the `factorial` problem looks like this:


## 命令型メソッド


命令型アプローチは、次のように記述する方法です。

`factorial`

Cのような言語では命令型のアプローチは多くの状態変化を伴い、変数は定期的に変更され、ループが頻繁に使用されます。Coconutでは、命令型のアプローチは

`factorial`

問題は次のようになります:

```coconut

def factorial(n):

    """Compute n! where n is an integer >= 0."""

    if n `isinstance` int and n >= 0:

        acc = 1

        for x in range(1, n+1):

            acc *= x

        return acc

    else:

        raise TypeError("the argument to factorial must be an integer >= 0")


# Test cases:

-1 |> factorial |> print  # TypeError

0.5 |> factorial |> print  # TypeError

0 |> factorial |> print  # 1

3 |> factorial |> print  # 6

```

ここで何が起こっているのかを詳しく調べる前に、実行してテストケースがチェックされるかどうかを確認しましょう。本当に Coconut プログラムを作成する場合は、実際のファイルを保存してコンパイルする必要がありますが、ここではただ遊んでいるだけなので、インタープリターにコピーして貼り付けてみましょう。ここでは2つ取得する必要があります

`TypeErrors`

、 それから

`1`

、 それから

`6`

。動作を確認できたので、何が起こっているのか見てみましょう。命令型アプローチは基本的に非機能的な方法であるため、Coconut はこの例を大幅に改善するのにはあまり役立ちません。しかし、ここでも、Coconutのインフィックス記法(関数を引数の間にバックティックで囲む)の使用は、

``

`isinstance`

整数

``

コードが少しすっきりして読みやすくなります。


#

## Recursive Method


The recursive approach is the first of the fundamentally functional approaches, in that it doesn't involve the state change and loops of the imperative approach. Recursive approaches avoid the need to change variables by making that variable change implicit in the recursive function call. Here's the recursive approach to the `factorial` problem in Coconut:


## 再帰的方法


再帰的アプローチは、命令型アプローチの状態変更やループを伴わないという点で、根本的に機能的なアプローチの最初のものです。再帰的アプローチでは、変数の変更を再帰関数呼び出しで暗黙的に行うことで、変数を変更する必要性を回避します。これが、

`factorial`

ココナッツの問題:

```coconut

def factorial(n):

    """Compute n! where n is an integer >= 0."""

    match n:

        case 0:

            return 1

        case x `isinstance` int if x > 0:

            return x * factorial(x-1)

    else:

        raise TypeError("the argument to factorial must be an integer >= 0")


# Test cases:

-1 |> factorial |> print  # TypeError

0.5 |> factorial |> print  # TypeError

0 |> factorial |> print  # 1

3 |> factorial |> print  # 6

```



Go ahead and copy and paste the code and tests into the interpreter. You should get the same test results as you got for the imperative version—but you can probably tell there's quite a lot more going on here than there. That's intentional: Coconut is intended for functional programming, not imperative programming, and so its new features are built to be most useful when programming in a functional style.


Let's take a look at the specifics of the syntax in this example. The first thing we see is `match n`. This statement starts a `case` block, in which only `case` statements can occur. Each `case` statement will attempt to match its given pattern against the value in the `case` block. Only the first successful match inside of any given `case` block will be executed. When a match is successful, any variable bindings in that match will also be performed. Additionally, as is true in this case, `case` statements can also have `if` guards that will check the given condition before the match is considered final. Finally, after the `case` block, an `else` statement is allowed, which will only be executed if no `case` statement is.


Specifically, in this example, the first `case` statement checks whether `n` matches to `0`. If it does, it executes `return 1`. Then the second `case` statement checks whether `n` matches to `` x `isinstance` int ``, which checks that `n` is an `int` (using `isinstance`) and assigns `x = n` if so, then checks whether `x > 0`, and if so, executes `return x * factorial(x-1)`. If neither of those two statements are executed, the `else` statement triggers and executes `raise TypeError("the argument to factorial must be an integer >= 0")`.


Although this example is very basic, pattern-matching is both one of Coconut's most powerful and most complicated features. As a general intuitive guide, it is helpful to think _assignment_ whenever you see the keyword `match`. A good way to showcase this is that all `match` statements can be converted into equivalent destructuring assignment statements, which are also valid Coconut. In this case, the destructuring assignment equivalent to the `factorial` function above would be:


コードとテストをコピーしてインタープリターに貼り付けてください。命令型バージョンと同じテスト結果が得られるはずですが、ここでは命令型バージョンよりもかなり多くのことが行われていることがわかるでしょう。これは意図的なものです。Coconut は命令型プログラミングではなく関数型プログラミングを目的としており、そのため、その新機能は関数型スタイルでプログラミングするときに最も役立つように構築されています。


この例の構文の詳細を見てみましょう。最初に目にするのは

`match n`

この文は

`case`

ブロックでは、

`case`

ステートメントが発生する可能性があります。各

`case`

文は与えられたパターンを

`case`

ブロック内で最初に成功した一致のみが

`case`

ブロックが実行されます。一致が成功すると、その一致内の変数バインディングも実行されます。さらに、この場合のように、

`case`

文には

`if`

マッチが最終とみなされる前に、与えられた条件をチェックするガード。最後に、

`case`

ブロック、

`else`

ステートメントが許可されており、

`case`

ステートメントは次のようになります。


具体的には、この例では、最初の

`case`

文は、

`n`

一致する

`0`

. 実行する場合は

`return 1`

. そして2番目

`case`

文は、

`n`

一致する

``

x

`isinstance`

整数

``

、これは次のことをチェックします

`n`

`int`

(使用

`isinstance`

)を割り当て、

`x = n`

もしそうなら、

`x > 0`

、もしそうなら、実行する

`return x * factorial(x-1)`

これら2つの文のどちらも実行されない場合は、

`else`

ステートメントがトリガーされ実行される

`raise TypeError("the argument to factorial must be an integer >= 0")`

.


この例は非常に基本的なものですが、パターンマッチングは Coconut の最も強力かつ最も複雑な機能の 1 つです。一般的な直感的なガイドとして、キーワードが出てくるたびに _assignment_ と考えると便利です。

`match`

これを示す良い方法は、

`match`

ステートメントは、同等の構造化代入ステートメントに変換することができ、これもココナッツとして有効です。この場合、構造化代入は、

`factorial`

上記の関数は次のようになります。

```coconut

def factorial(n):

    """Compute n! where n is an integer >= 0."""

    try:

        # The only value that can be assigned to 0 is 0, since 0 is an

        # immutable constant; thus, this assignment fails if n is not 0.

        0 = n

    except MatchError:

        pass

    else:

        return 1

    try:

        # This attempts to assign n to x, which has been declared to be

        # an int; since only an int can be assigned to an int, this

        # fails if n is not an int.

        x `isinstance` int = n

    except MatchError:

        pass

    else: if x > 0:  # in Coconut, statements can be nested on the same line

        return x * factorial(x-1)

    raise TypeError("the argument to factorial must be an integer >= 0")


# Test cases:

-1 |> factorial |> print  # TypeError

0.5 |> factorial |> print  # TypeError

0 |> factorial |> print  # 1

3 |> factorial |> print  # 6

```



First, copy and paste! While this destructuring assignment equivalent should work, it is much more cumbersome than `match` statements when you expect that they'll fail, which is why `match` statement syntax exists. But the destructuring assignment equivalent illuminates what exactly the pattern-matching is doing, by making it clear that `match` statements are really just fancy destructuring assignment statements. In fact, to be explicit about using destructuring assignment instead of normal assignment, the `match` keyword can be put before a destructuring assignment statement to signify it as such.


It will be helpful to, as we continue to use Coconut's pattern-matching and destructuring assignment statements in further examples, think _assignment_ whenever you see the keyword `match`.


Next, we can make a couple of simple improvements to our `factorial` function. First, we don't actually need to assign `x` as a new variable, since it has the same value as `n`, so if we use `_` instead of `x`, Coconut won't ever actually assign the variable. Thus, we can rewrite our `factorial` function as:


まず、コピーして貼り付けます。この分割代入の同等の機能は動作するはずですが、

`match`

失敗すると予想されるときに声明を出すのは、

`match`

文の構文は存在します。しかし、分割代入に相当するものは、パターンマッチングが正確に何をしているかを明らかにします。

`match`

ステートメントは実際には単なる派手な分割代入ステートメントです。実際、通常の代入ではなく分割代入を使用することを明示的に示すには、

`match`

キーワードは、デストラクチャリング代入ステートメントの前に置くことで、そのことを示すことができます。


以降の例で Coconut のパターン マッチングとデストラクチャリング代入ステートメントを使い続けるので、キーワードが出てくるたびに _assignment_ と考えると便利です。

`match`

.


次に、いくつかの簡単な改善を加えます

`factorial`

まず、実際に割り当てる必要はありません

`x`

同じ値を持つため、新しい変数として

`n`

なので、

`_`

の代わりに

`x`

、Coconutは実際に変数を割り当てることはありません。したがって、

`factorial`

機能:

```coconut

def factorial(n):

    """Compute n! where n is an integer >= 0."""

    match n:

        case 0:

            return 1

        case _ `isinstance` int if n > 0:

            return n * factorial(n-1)

    else:

        raise TypeError("the argument to factorial must be an integer >= 0")


# Test cases:

-1 |> factorial |> print  # TypeError

0.5 |> factorial |> print  # TypeError

0 |> factorial |> print  # 1

3 |> factorial |> print  # 6

```



Copy, paste! This new `factorial` function should behave exactly the same as before.


Second, we can replace the `` _ `isinstance` int `` pattern with the class pattern `int()`, which, when used with no arguments like that, is equivalent. Thus, we can again rewrite our `factorial` function to:


コピーして貼り付けてください!この新しい

`factorial`

関数は以前とまったく同じように動作するはずです。


次に、

``

`isinstance`

整数

``

クラスパターンのパターン

`int()`

は、そのような引数なしで使用された場合、同等です。したがって、再び書き直すことができます。

`factorial`

機能:

```coconut

def factorial(n):

    """Compute n! where n is an integer >= 0."""

    match n:

        case 0:

            return 1

        case int() if n > 0:

            return n * factorial(n-1)

    else:

        raise TypeError("the argument to factorial must be an integer >= 0")


# Test cases:

-1 |> factorial |> print  # TypeError

0.5 |> factorial |> print  # TypeError

0 |> factorial |> print  # 1

3 |> factorial |> print  # 6

```



Up until now, for the recursive method, we have only dealt with pattern-matching, but there's actually another way that Coconut allows us to improve our `factorial` function. Coconut performs automatic tail call optimization, which means that whenever a function directly returns a call to another function, Coconut will optimize away the additional call. Thus, we can improve our `factorial` function by rewriting it to use a tail call:


これまで、再帰的な方法ではパターンマッチングのみを扱ってきましたが、実はCoconutでは別の方法で改善することができます。

`factorial`

関数。Coconutは自動末尾呼び出し最適化を実行します。つまり、関数が別の関数への呼び出しを直接返すたびに、Coconutは追加の呼び出しを最適化して取り除きます。これにより、

`factorial`

関数を末尾呼び出しを使用するように書き直すことで、

```coconut

def factorial(n, acc=1):

    """Compute n! where n is an integer >= 0."""

    match n:

        case 0:

            return acc

        case int() if n > 0:

            return factorial(n-1, acc*n)

    else:

        raise TypeError("the argument to factorial must be an integer >= 0")


# Test cases:

-1 |> factorial |> print  # TypeError

0.5 |> factorial |> print  # TypeError

0 |> factorial |> print  # 1

3 |> factorial |> print  # 6

```

コピーして貼り付けてください!この新しい

`factorial`

この関数は、例外が発生しないことを除いて、元のバージョンと同じです。

`RuntimeError`

Python の最大再帰深度に達したため、Coconut は末尾呼び出しを最適化して取り除くためです。


#

## Iterative Method


The other main functional approach is the iterative one. Iterative approaches avoid the need for state change and loops by using higher-order functions, those that take other functions as their arguments, like `map` and `reduce`, to abstract out the basic operations being performed. In Coconut, the iterative approach to the `factorial` problem is:


## 反復法


もう 1 つの主な関数アプローチは反復法です。反復アプローチでは、他の関数を引数として取る高階関数を使用することで、状態の変更やループの必要性を回避します。

`map`

そして

`reduce`

実行されている基本的な操作を抽象化します。Coconutでは、

`factorial`

問題は:

```coconut

def factorial(n):

    """Compute n! where n is an integer >= 0."""

    match n:

        case 0:

            return 1

        case int() if n > 0:

            return range(1, n+1) |> reduce$(*)

    else:

        raise TypeError("the argument to factorial must be an integer >= 0")


# Test cases:

-1 |> factorial |> print  # TypeError

0.5 |> factorial |> print  # TypeError

0 |> factorial |> print  # 1

3 |> factorial |> print  # 6

```



Copy, paste! This definition differs from the recursive definition only by one line. That's intentional: because both the iterative and recursive approaches are functional approaches, Coconut can provide a great assist in making the code cleaner and more readable. The one line that differs is this one:


コピーして貼り付けてください。この定義は、再帰定義と 1 行だけ異なります。これは意図的なものです。反復アプローチと再帰アプローチはどちらも関数型アプローチであるため、Coconut はコードをよりクリーンで読みやすくするのに大いに役立ちます。異なる 1 行は次の 1 行です。

```coconut

return range(1, n+1) |> reduce$(*)

```



Let's break down what's happening on this line. First, the `range` function constructs an iterator of all the numbers that need to be multiplied together. Then, it is piped into the function `reduce$(*)`, which does that multiplication. But how? What is `reduce$(*)`?


We'll start with the base, the `reduce` function. `reduce` used to exist as a built-in in Python 2, and Coconut brings it back. `reduce` is a higher-order function that takes a function of two arguments as its first argument, and an iterator as its second argument, and applies that function to the given iterator by starting with the first element, and calling the function on the accumulated call so far and the next element, until the iterator is exhausted. Here's a visual representation:


この路線で何が起こっているのか分析してみましょう。まず、

`range`

関数は掛け合わせる必要のあるすべての数値のイテレータを構築します。そして、それは関数にパイプされます。

`reduce$(*)`

は、その掛け算を行います。しかし、どのように?

`reduce$(*)`

?


まずはベースから始めましょう。

`reduce`

関数。

`reduce`

以前は Python 2 に組み込まれていましたが、Coconut で復活しました。

`reduce`

は、最初の引数として 2 つの引数を持つ関数、2 番目の引数として反復子を受け取り、その関数を指定された反復子に適用する高階関数です。この関数は、最初の要素から開始し、反復子がなくなるまで、これまでの累積呼び出しと次の要素に対して関数を呼び出します。次に、視覚的に表したものを示します。

```coconut

reduce(f, (a, b, c, d))


acc                 iter

                    (a, b, c, d)

a                   (b, c, d)

f(a, b)             (c, d)

f(f(a, b), c)       (d)

f(f(f(a, b), c), d)


return acc

```



Now let's take a look at what we do to `reduce` to make it multiply all the numbers we feed into it together. The Coconut code that we saw for that was `reduce$(*)`. There are two different Coconut constructs being used here: the operator function for multiplication in the form of `(*)`, and partial application in the form of `$`.


First, the operator function. In Coconut, a function form of any operator can be retrieved by surrounding that operator in parentheses. In this case, `(*)` is roughly equivalent to `lambda x, y: x*y`, but much cleaner and neater. In Coconut's lambda syntax, `(*)` is also equivalent to `(x, y) => x*y`, which we will use from now on for all lambdas, even though both are legal Coconut, because Python's `lambda` statement is too ugly and bulky to use regularly.


_Note: If Coconut's `--strict` mode is enabled, which will force your code to obey certain cleanliness standards, it will raise an error whenever Python `lambda` statements are used._


Second, the partial application. Think of partial application as _lazy function calling_, and `$` as the _lazy-ify_ operator, where lazy just means "don't evaluate this until you need to." In Coconut, if a function call is prefixed by a `$`, like in this example, instead of actually performing the function call, a new function is returned with the given arguments already provided to it, so that when it is then called, it will be called with both the partially-applied arguments and the new arguments, in that order. In this case, `reduce$(*)` is roughly equivalent to `(*args, **kwargs) => reduce((*), *args, **kwargs)`.


_You can partially apply arguments in any order using `?` in place of missing arguments, as in `to_binary = int$(?, 2)`._


Putting it all together, we can see how the single line of code


それでは、私たちが何をするのか見てみましょう

`reduce`

入力した数字を掛け合わせるために使います。私たちが見たCoconutのコードは

`reduce$(*)`

ここでは2つの異なるココナッツ構造が使用されています。乗算の演算子関数は次の形式です。

`(*)`

、および部分的な適用の形で

`$`

.


まず、演算子関数です。Coconut では、演算子を括弧で囲むことで、任意の演算子の関数形式を取得できます。この場合、

`(*)`

は、ほぼ

`lambda x, y: x*y`

ですが、よりクリーンですっきりしています。Coconutのラムダ構文では、

`(*)`

は以下と同等である。

`(x, y) => x*y`

は、どちらも合法的なCoconutであるにもかかわらず、Pythonの

`lambda`

ステートメントは見苦しく、かさばりすぎて、日常的に使用できません。


_注: Coconut の

`--strict`

モードが有効になっていると、コードは特定のクリーンさの基準に従うよう強制され、Python

`lambda`

_


2番目は部分適用です。部分適用は_遅延関数呼び出し_と考えてください。

`$`

_lazy-ify_演算子として、lazyは単に「必要になるまで評価しない」という意味です。Coconutでは、関数呼び出しの前に

`$`

この例のように、実際に関数呼び出しを実行する代わりに、指定された引数がすでに提供された新しい関数が返され、その後呼び出されると、部分的に適用された引数と新しい引数の両方がその順序で呼び出されます。この場合、

`reduce$(*)`

は、ほぼ

`(*args, **kwargs) => reduce((*), *args, **kwargs)`

.


_引数を任意の順序で部分的に適用するには、

`?`

引数が足りない場合は、次のように

`to_binary = int$(?, 2)`

._


これらをまとめると、たった1行のコードが

```coconut

range(1, n+1) |> reduce$(*)

```

状態やループを使用せず、高階関数のみを使用して、真の関数型スタイルで適切な階乗を計算できます。ここで使用する部分適用のようなツールを提供することで(

`$`

)、パイプライン型プログラミング(

`|>`

)、高階関数(

`reduce`

)、および演算子関数(

`(*)`

)、Coconut を使用すると、このような関数型プログラミングをクリーンかつ簡潔に、簡単に実行できます。


#

## `addpattern` Method


While the iterative approach is very clean, there are still some bulky pieces—looking at the iterative version below, you can see that it takes three entire indentation levels to get from the function definition to the actual objects being returned:


`addpattern`

メソッド


反復的なアプローチは非常に簡潔ですが、まだかさばる部分がいくつかあります。以下の反復バージョンを見ると、関数定義から返される実際のオブジェクトに到達するまでに 3 つのインデント レベルが必要であることがわかります。

```coconut

def factorial(n):

    """Compute n! where n is an integer >= 0."""

    match n:

        case 0:

            return 1

        case int() if n > 0:

            return range(1, n+1) |> reduce$(*)

    else:

        raise TypeError("the argument to factorial must be an integer >= 0")

```



By making use of the [Coconut `addpattern` syntax](./DOCS.md#addpattern), we can take that from three indentation levels down to one. Take a look:


[ココナッツ

`addpattern`

構文](./DOCS.md#addpattern) を使用すると、インデント レベルを 3 から 1 に減らすことができます。見てみましょう:

```coconut

def factorial(0) = 1


addpattern def factorial(int() as n if n > 0) =

    """Compute n! where n is an integer >= 0."""

    range(1, n+1) |> reduce$(*)


# Test cases:

-1 |> factorial |> print  # MatchError

0.5 |> factorial |> print  # MatchError

0 |> factorial |> print  # 1

3 |> factorial |> print  # 6

```


Copy, paste! This should work exactly like before, except now it raises `MatchError` as a fall through instead of `TypeError`. There are three major new concepts to talk about here: `addpattern`, of course, assignment function notation, and pattern-matching function definition—how both of the functions above are defined.


First, assignment function notation. This one's pretty straightforward. If a function is defined with an `=` instead of a `:`, the last line is required to be an expression, and is automatically returned.


Second, pattern-matching function definition. Pattern-matching function definition does exactly that—pattern-matches against all the arguments that are passed to the function. Unlike normal function definition, however, if the pattern doesn't match (if for example the wrong number of arguments are passed), your function will raise a `MatchError`. Finally, like destructuring assignment, if you want to be more explicit about using pattern-matching function definition, you can add a `match` before the `def`. In this case, we're also using one new pattern-matching construct, the `as` match, which matches against the pattern on the left and assigns the result to the name on the right.


Third, `addpattern`. `addpattern` creates a new pattern-matching function by adding the new pattern as an additional case to the old pattern-matching function it is replacing. Thus, `addpattern` can be thought of as doing exactly what it says—it adds a new pattern to an existing pattern-matching function.


Finally, not only can we rewrite the iterative approach using `addpattern`, as we did above, we can also rewrite the recursive approach using `addpattern`, like so:


コピーして貼り付けてください。これは以前とまったく同じように動作しますが、今度は

`MatchError`

代わりに、

`TypeError`

ここで説明する新しい主要な概念は 3 つあります。

`addpattern`

もちろん、代入関数表記法とパターンマッチング関数定義法は、上記の両方の関数がどのように定義されているかを示しています。


まず、代入関数表記法。これは非常に簡単です。関数が

`=`

代わりに

`:`

最後の行は式である必要があり、自動的に返されます。


2番目は、パターンマッチング関数定義です。パターンマッチング関数定義は、まさにそのとおりです。つまり、関数に渡されるすべての引数に対してパターンマッチングを行います。ただし、通常の関数定義とは異なり、パターンが一致しない場合(たとえば、間違った数の引数が渡された場合)、関数はエラーを発生させます。

`MatchError`

最後に、分割代入と同様に、パターンマッチング関数定義の使用をより明示的にしたい場合は、

`match`

前に

`def`

この場合、新しいパターンマッチング構造も使用しています。

`as`

マッチは左側のパターンと一致し、その結果を右側の名前に割り当てます。


3 番目

`addpattern`

`addpattern`

置き換え対象の古いパターンマッチング関数に新しいパターンを追加することで、新しいパターンマッチング関数を作成します。つまり、

`addpattern`

まさにその名の通り、既存のパターンマッチング関数に新しいパターンを追加するものと考えることができます。


最後に、反復アプローチを次のように書き換えるだけでなく、

`addpattern`

上で行ったように、再帰的なアプローチを次のように書き直すこともできる。

`addpattern`

次のようにします。

```coconut

def factorial(0) = 1


addpattern def factorial(int() as n if n > 0) =

    """Compute n! where n is an integer >= 0."""

    n * factorial(n - 1)


# Test cases:

-1 |> factorial |> print  # MatchError

0.5 |> factorial |> print  # MatchError

0 |> factorial |> print  # 1

3 |> factorial |> print  # 6

```

コピーして貼り付けてください。上記と全く同じように動作するはずですが、

`TypeError`

置き換えられた

`MatchError`

## ケーススタディ2:

`quick_sort`

2 番目のケーススタディでは、[クイック ソート アルゴリズム](https://en.ウィキペディア。org/wiki/Quicksort を参照。2つのバージョンを実装します。まず、

`quick_sort`

リストを入力としてリストを出力する関数、そして2番目に

`quick_sort`

イテレータを受け取り、イテレータを出力する関数。


#

## Sorting a Sequence


First up is `quick_sort` for lists. We're going to use a recursive `addpattern`-based approach to tackle this problem—a similar approach to the very last `factorial` function we wrote, using `addpattern` to reduce the amount of indentation we're going to need. Without further ado, here's our implementation of `quick_sort` for lists:


## シーケンスのソート


まずは

`quick_sort`

リストの場合、再帰的な

`addpattern`

この問題に取り組むためのアプローチは、前回の

`factorial`

関数は次のように記述します。

`addpattern`

必要なインデントの量を減らすためです。さっそく、実装例を示します。

`quick_sort`

リストの場合:

```coconut

def quick_sort([]) = []


addpattern def quick_sort([head] + tail) =

    """Sort the input sequence using the quick sort algorithm."""

    quick_sort(left) + [head] + quick_sort(right) where:

        left = [x for x in tail if x < head]

        right = [x for x in tail if x >= head]


# Test cases:

[] |> quick_sort |> print  # []

[3] |> quick_sort |> print  # [3]

[0,1,2,3,4] |> quick_sort |> print  # [0,1,2,3,4]

[4,3,2,1,0] |> quick_sort |> print  # [0,1,2,3,4]

[3,0,4,2,1] |> quick_sort |> print  # [0,1,2,3,4]

```

コピー、貼り付け!2つの新機能:ヘッドテールパターンマッチングと

`where`

声明。初め、

`where`

声明は非常に率直です。実際、上記のコードから、それらが何をするのかはすでにわかっていると思います。あ

`where`

ステートメントは、一連の代入ステートメントのコンテキスト内で何かを計算する方法にすぎません。2つ目は、ヘッドテールパターンマッチングです。

`[head] + tail`

は、変数に追加されたリストまたはタプルの形式に従います。パターン マッチングのコンテキストでこれが出現すると、照合される値はシーケンスとして扱われ、リストまたはタプルはそのシーケンスの先頭と照合され、残りの部分は変数にバインドされます。この場合、head-tail パターンを使用してヘッドを削除し、それをリストの残りの部分を分割するためのピボットとして使用できるようにします。


#

## Sorting an Iterator


Now it's time to try `quick_sort` for iterators. Our method for tackling this problem is going to be a combination of the recursive and iterative approaches we used for the `factorial` problem, in that we're going to be lazily building up an iterator, and we're going to be doing it recursively. Here's the code:


## イテレータのソート


さあ、試してみましょう

`quick_sort`

この問題に取り組むための私たちの方法は、再帰的アプローチと反復的アプローチを組み合わせたものになります。

`factorial`

問題は、反復子を遅延的に構築し、それを再帰的に実行するという点です。コードは次のとおりです。

```coconut

def quick_sort(l):

    """Sort the input iterator using the quick sort algorithm."""

    match [head] :: tail in l:

        tail = reiterable(tail)

        yield from quick_sort(left) :: [head] :: quick_sort(right) where:

            left = (x for x in tail if x < head)

            right = (x for x in tail if x >= head)

    # By yielding nothing if the match falls through, we implicitly return an empty iterator.


# Test cases:

[] |> quick_sort |> list |> print  # []

[3] |> quick_sort |> list |> print  # [3]

[0,1,2,3,4] |> quick_sort |> list |> print  # [0,1,2,3,4]

[4,3,2,1,0] |> quick_sort |> list |> print  # [0,1,2,3,4]

[3,0,4,2,1] |> quick_sort |> list |> print  # [0,1,2,3,4]

```


Copy, paste! This `quick_sort` algorithm works uses a bunch of new constructs, so let's go over them.


First, the `::` operator, which appears here both in pattern-matching and by itself. In essence, the `::` operator is lazy `+` for iterators. On its own, it takes two iterators and concatenates, or chains, them together, and it does this lazily, not evaluating anything until its needed, so it can be used for making infinite iterators. In pattern-matching, it inverts that operation, destructuring the beginning of an iterator into a pattern, and binding the rest of that iterator to a variable.


Which brings us to the second new thing, `match ... in ...` notation. The notation


コピーして貼り付けてください。

`quick_sort`

アルゴリズムの動作には多くの新しい構成要素が使用されているので、それらについて見ていきましょう。


まず、

`::`

演算子はパターンマッチングと単独でもここに現れます。本質的には、

`::`

オペレーターは怠け者

`+`

イテレータ用です。それ自体では、2 つのイテレータを連結または連鎖しますが、これは遅延実行され、必要になるまで何も評価しないため、無限イテレータの作成に使用できます。パターン マッチングでは、その操作を反転し、イテレータの先頭をパターンに分解し、そのイテレータの残りを変数にバインドします。


これで、2 つ目の新機能に移ります。

`match ... in ...`

表記法。表記法

```coconut

match pattern in item:

    <body>

else:

    <else>

```


is shorthand for


は、

```coconut

match item:

    case pattern:

        <body>

else:

    <else>

```


that avoids the need for an additional level of indentation when only one `match` is being performed.


The third new construct is the [Coconut built-in `reiterable`](./DOCS.md#reiterable). There is a problem in doing immutable functional programming with Python iterators: whenever an element of an iterator is accessed, it's lost. `reiterable` solves this problem by allowing the iterable it's called on to be iterated over multiple times while still yielding the same result each time


Finally, although it's not a new construct, since it exists in Python 3, the use of `yield from` here deserves a mention. In Python, `yield` is the statement used to construct iterators, functioning much like `return`, with the exception that multiple `yield`s can be encountered, and each one will produce another element. `yield from` is very similar, except instead of adding a single element to the produced iterator, it adds another whole iterator.


Putting it all together, here's our `quick_sort` function again:


これにより、1つのみの場合に追加のインデントレベルが必要になるのを回避できます。

`match`

実行されています。


3番目の新しい構成は、[ココナッツ組み込み

`reiterable`

](./DOCS.md#reiterable)。Python イテレータを使用して不変の関数型プログラミングを行うと、イテレータの要素にアクセスするたびにその要素が失われるという問題があります。

`reiterable`

この問題は、呼び出された反復可能オブジェクトを複数回反復処理しても毎回同じ結果を返すようにすることで解決されます


最後に、これは新しい構造ではありませんが、Python 3 に存在するため、

`yield from`

ここで言及する価値がある。Pythonでは、

`yield`

はイテレータを構築するために使われる文であり、

`return`

ただし、複数の

`yield`

に遭遇する可能性があり、それぞれが別の要素を生成します。

`yield from`

非常に似ていますが、生成されたイテレータに単一の要素を追加する代わりに、別のイテレータ全体を追加します。


すべてをまとめると、次のようになります。

`quick_sort`

もう一度機能させる:

```coconut

def quick_sort(l):

    """Sort the input iterator using the quick sort algorithm."""

    match [head] :: tail in l:

        tail = reiterable(tail)

        yield from quick_sort(left) :: [head] :: quick_sort(right) where:

            left = (x for x in tail if x < head)

            right = (x for x in tail if x >= head)

    # By yielding nothing if the match falls through, we implicitly return an empty iterator.

```

この関数はまず分割を試みる

`l`

最初の要素と残りの反復子に分割されます。もし

`l`

空の反復子の場合、一致は失敗し、フォールスルーして空の反復子が生成されます (これが関数が基本ケースを処理する方法です)。それ以外の場合は、反復子の残りのコピーを作成し、(最初の要素より小さい残りのすべての要素のクイック ソート)、(最初の要素)、および (最初の要素より大きい残りのすべての要素のクイック ソート) の結合を生成します。ここで使用されている基本的なアプローチ、つまり反復子と再帰を多用するアプローチには、従来の命令型アプローチとは対照的に、数多くの利点があります。まず、私たちのアプローチは、_何_を_説明しているか_をより明確かつ読みやすくしています。

`quick_sort`

_how_の代わりに

`quick_sort`

実装できる可能性があります。第二に、私たちのアプローチは、

`quick_sort`

必要になるまでデータは評価されません。最後に、これは関係ありませんが

`quick_sort`

これは他の多くのケースにも関連しており、その例はこのチュートリアルの後半で説明しますが、私たちのアプローチでは、無限級数を有限級数であるかのように扱うことができます。そして、Coconut は、このような有利な関数型アプローチでのプログラミングを大幅に容易にします。この例では、Coconutのパターンマッチングにより、与えられたイテレータを簡単に分割することができ、Coconutの

`::`

反復子結合演算子を使用すると、簡単に並べ替えた順序に戻すことができます。

## ケーススタディ3:

`vector`

パート I


次のケース スタディでは、少し異なることを行います。関数を定義する代わりに、オブジェクトを作成します。具体的には、すべての基本的なベクトル演算をサポートする不変の n ベクトルを実装してみます。


関数型プログラミングでは、Python の文字列やタプルのように、一度作成すると変更できない不変のオブジェクトを定義することが非常に望ましいことがよくあります。文字列やタプルと同様に、不変のオブジェクトはさまざまな理由で役立ちます。


- 変更されないことが保証されるため、理解しやすい。

- ハッシュ可能でピクル可能なので、キーとして使用したりシリアル化したりできます。


- オーバーヘッドがはるかに少ないため、大幅に効率的です。


- パターン マッチングと組み合わせると、_代数的データ型_ と呼ばれるものとして使用して、大規模で複雑なデータ構造を簡単に構築し、照合できます。


#

## 2-Vector


Coconut's `data` statement brings the power and utility of _immutable, algebraic data types_ to Python, and it is this that we will be using to construct our `vector` type. The demonstrate the syntax of `data` statements, we'll start by defining a simple 2-vector. Our vector will have one special method `__abs__` which will compute the vector's magnitude, defined as the square root of the sum of the squares of the elements. Here's our 2-vector:


## 2-ベクター


ココナッツ

`data`

ステートメントは、Pythonに_不変の代数的データ型_のパワーと有用性をもたらし、これを使用して、

`vector`

型。構文のデモンストレーション

`data`

ステートメントでは、まず単純な2ベクトルを定義することから始めます。ベクトルには特別なメソッドが1つあります

`__abs__`

これはベクトルの大きさを計算します。大きさは要素の二乗の合計の平方根として定義されます。2 ベクトルは次のようになります。

```coconut

data vector2(x, y):

    """Immutable 2-vector."""

    def __abs__(self) =

        """Return the magnitude of the 2-vector."""

        (self.x**2 + self.y**2)**0.5


# Test cases:

vector2(1, 2) |> print  # vector2(x=1, y=2)

vector2(3, 4) |> abs |> print  # 5

vector2(1, 2) |> fmap$(x => x*2) |> print  # vector2(x=2, y=4)

v = vector2(2, 3)

v.x = 7  # AttributeError

```



Copy, paste! This example shows the basic syntax of `data` statements:


コピーして貼り付けてください。この例は、

`data`

声明:

```coconut

data <name>(<attributes>):

    <body>

```

どこ

`<name>`

そして

`<body>`

同等のものと同じ

`class`

定義ですが

`<attributes>`

これらは、コンストラクターが引数として受け取ることができるようにするための、データ型のさまざまな属性です。この場合、

`vector2`

2つの属性を持つデータ型です。

`x`

そして

`y`

定義された1つの方法で、

`__abs__`

、大きさを計算します。テストケースが示すように、インスタンスを作成、印刷することはできますが、変更することはできません。

`vector2`

。ここで注目すべきもう一つの点は、[ココナッツ内蔵

`fmap`

/ドキュメント。md#fmap)。

`fmap`

代数データ型に関数をマッピングできます。ココナッツ

`data`

型は反復処理をサポートしているので、標準の

`map`

これらに対しても動作しますが、同じデータ型の別のオブジェクトは返しません。この場合、

`fmap`

単に

`map`

オブジェクトのコンストラクタへの呼び出しも追加されます。


#

## n-Vector Constructor


Now that we've got the 2-vector under our belt, let's move to back to our original, more complicated problem: n-vectors, that is, vectors of arbitrary length. We're going to try to make our n-vector support all the basic vector operations, but we'll start out with just the `data` definition and the constructor:


## n-ベクトルコンストラクタ


2-ベクトルについては理解できたので、元のより複雑な問題であるn-ベクトル、つまり任意の長さのベクトルに戻りましょう。n-ベクトルですべての基本的なベクトル演算をサポートするようにするつもりですが、まずは

`data`

定義とコンストラクタ:

```coconut

data vector(*pts):

    """Immutable n-vector."""

    def __new__(cls, *pts):

        """Create a new vector from the given pts."""

        match [v `isinstance` vector] in pts:

            return v  # vector(v) where v is a vector should return v

        else:

            return pts |*> makedata$(cls)  # accesses base constructor


# Test cases:

vector(1, 2, 3) |> print  # vector(*pts=(1, 2, 3))

vector(4, 5) |> vector |> print  # vector(*pts=(4, 5))

```

コピー、貼り付け!ここでの大きな新しい点は、書き方です

`data`

コンストラクター。以来

`data`

型は不変であり、

`__init__`

建設は機能しません。代わりに、別の特別な方法

`__new__`

が使用されます。これは新しく構築されたインスタンスを返す必要があり、ほとんどのメソッドとは異なり、最初の引数としてオブジェクトではなくクラスを受け取ります。以来

`__new__`

完全に構築されたインスタンスを返す必要がある場合、ほとんどの場合、基礎となるインスタンスにアクセスする必要があります。

`data`

コンストラクタ。これを実現するために、Coconutは[組み込み

`makedata`

関数](。/ドキュメント。md#makedata)はデータ型を受け取り、その基になる

`data`

残りの引数を持つコンストラクター。この場合、コンストラクタは、他の

`vector`

渡された場合はそれを返します。そうでない場合は、基礎となるコンストラクタに引数を渡した結果を返します。その形式は次のようになります。

`vector(*pts)`

データ型をこのように宣言したためです。シーケンス パターン マッチングを使用して、シーケンスの内容と一致するパターンのリストまたはタプルである単一のベクトルが渡されたかどうかを判断します。ここで指摘しておくべき重要な落とし穴が1つあります。この場合、

``

`isinstance`

ベクター

``

それよりも

`vector() as v`

なぜなら、後で見るように、次のようなパターンは

`vector()`

異なる行動をとる

`data`

通常のクラスよりも多くの種類があります。この場合、

`vector()`

任意のベクトルではなく、長さがゼロのベクトルにのみ一致します。ここで使用されているもう一つの新しい構造は

`|*>`

、またはスターパイプ演算子は、通常のパイプとまったく同じように機能しますが、関数を 1 つの引数で呼び出すのではなく、渡されたシーケンス内の要素と同じ数の引数で呼び出します。違いは

`|>`

そして

`|*>`

は、

`f(args)`

そして

`f(*args)`

.


#

## n-Vector Methods


Now that we have a constructor for our n-vector, it's time to write its methods. First up is `__abs__`, which should compute the vector's magnitude. This will be slightly more complicated than with the 2-vector, since we have to make it work over an arbitrary number of `pts`. Fortunately, we can use Coconut's pipeline-style programming and partial application to make it simple:


## n-ベクトルメソッド


n-ベクトルのコンストラクタができたので、次はそのメソッドを記述します。まずは

`__abs__`

はベクトルの大きさを計算する。これは2次元ベクトルの場合よりも少し複雑になる。任意の数の

`pts`

幸いなことに、Coconut のパイプライン スタイルのプログラミングと部分適用を使用すると、簡単に行うことができます。

```coconut

    def __abs__(self) =

        """Return the magnitude of the vector."""

        self.pts |> map$(.**2) |> sum |> (.**0.5)

```


The basic algorithm here is map square over each element, sum them all, then square root the result. The one new construct here is the `(.**2)` and `(.**0.5)` syntax, which are effectively equivalent to `(x => x**2)` and `(x => x**0.5)`, respectively (though the `(.**2)` syntax produces a pickleable object). This syntax works for all [operator functions](./DOCS.md#operator-functions), so you can do things like `(1-.)` or `(cond() or .)`.


Next up is vector addition. The goal here is to add two vectors of equal length by adding their components. To do this, we're going to make use of Coconut's ability to perform pattern-matching, or in this case destructuring assignment, to data types, like so:


ここでの基本的なアルゴリズムは、各要素を2乗して合計し、その結果を平方根にするというものです。ここでの新しい構成は、

`(.**2)`

そして

`(.**0.5)`

構文は実質的に

`(x => x**2)`

そして

`(x => x**0.5)`

、それぞれ(ただし、

`(.**2)`

構文はpickle可能なオブジェクトを生成します。この構文はすべての[演算子関数](./DOCS.md#operator-functions)で機能するため、次のようなことができます。

`(1-.)`

または

`(cond() or .)`

.


次はベクトルの加算です。ここでの目標は、長さが同じ 2 つのベクトルの要素を加算して加算することです。これを行うには、次のように、Coconut のパターン マッチング機能、またはこの場合はデータ型への構造化代入機能を使用します。

```coconut

    def __add__(self, vector(*other_pts)

                if len(other_pts) == len(self.pts)) =

        """Add two vectors together."""

        map((+), self.pts, other_pts) |*> vector

```



There are a couple of new constructs here, but the main notable one is the pattern-matching `vector(*other_pts)` which showcases the syntax for pattern-matching against data types: it mimics exactly the original `data` declaration of that data type. In this case, `vector(*other_pts)` will only match a vector, raising a `MatchError` otherwise, and if it does match a vector, will assign the vector's `pts` attribute to the variable `other_pts`.


Next is vector subtraction, which is just like vector addition, but with `(-)` instead of `(+)`:


ここではいくつかの新しい構造がありますが、最も注目すべきはパターンマッチングです。

`vector(*other_pts)`

これは、データ型に対するパターンマッチングの構文を示しています。これは、元の

`data`

そのデータ型の宣言。この場合、

`vector(*other_pts)`

ベクトルのみにマッチし、

`MatchError`

それ以外の場合、ベクトルに一致する場合は、ベクトルの

`pts`

変数に属性を付ける

`other_pts`

.


次はベクトル減算です。これはベクトル加算と似ていますが、

`(-)`

の代わりに

`(+)`

```coconut

    def __sub__(self, vector(*other_pts)

                if len(other_pts) == len(self.pts)) =

        """Subtract one vector from another."""

        map((-), self.pts, other_pts) |*> vector

```



One thing to note here is that unlike the other operator functions, `(-)` can either mean negation or subtraction, the meaning of which will be inferred based on how many arguments are passed, 1 for negation, 2 for subtraction. To show this, we'll use the same `(-)` function to implement vector negation, which should simply negate each element:


ここで注目すべき点は、他の演算子関数とは異なり、

`(-)`

は否定または減算のいずれかを意味し、その意味は渡される引数の数に基づいて推測されます。否定の場合は1、減算の場合は2です。これを示すために、同じ

`(-)`

ベクトルの否定を実装する関数。これは各要素を単純に否定するものです。

```coconut

    def __neg__(self) =

        """Retrieve the negative of the vector."""

        self.pts |> map$(-) |*> vector

```



The last method we'll implement is multiplication. This one is a little bit tricky, since mathematically, there are a whole bunch of different ways to multiply vectors. For our purposes, we're just going to look at two: between two vectors of equal length, we want to compute the dot product, defined as the sum of the corresponding elements multiplied together, and between a vector and a scalar, we want to compute the scalar multiple, which is just each element multiplied by that scalar. Here's our implementation:


最後に実装するメソッドは乗算です。これは少しトリッキーです。数学的には、ベクトルを乗算する方法はたくさんあるからです。ここでは、2 つだけ見ていきます。長さが同じ 2 つのベクトルの間では、対応する要素を乗算した合計として定義されるドット積を計算します。また、ベクトルとスカラーの間では、各要素をそのスカラーで乗算したスカラー倍数を計算します。実装は次のとおりです。

```coconut

    def __mul__(self, other):

        """Scalar multiplication and dot product."""

        match vector(*other_pts) in other:

            assert len(other_pts) == len(self.pts)

            return map((*), self.pts, other_pts) |> sum  # dot product

        else:

            return self.pts |> map$(.*other) |*> vector  # scalar multiple

    def __rmul__(self, other) =

        """Necessary to make scalar multiplication commutative."""

        self * other

```



The first thing to note here is that unlike with addition and subtraction, where we wanted to raise an error if the vector match failed, here, we want to do scalar multiplication if the match fails, so instead of using destructuring assignment, we use a `match` statement. The second thing to note here is the combination of pipeline-style programming, partial application, operator functions, and higher-order functions we're using to compute the dot product and scalar multiple. For the dot product, we map multiplication over the two vectors, then sum the result. For the scalar multiple, we take the original points, map multiplication by the scalar over them, then use them to make a new vector.


Finally, putting everything together:


ここで最初に注意すべきことは、加算と減算ではベクトルの一致が失敗した場合にエラーを発生させたいのに対し、ここでは一致が失敗した場合にスカラー乗算を行いたいので、分割代入を使用する代わりに、

`match`

ステートメント。ここで 2 番目に注意すべき点は、ドット積とスカラー倍数を計算するために使用しているパイプライン スタイルのプログラミング、部分適用、演算子関数、および高階関数の組み合わせです。ドット積の場合、2 つのベクトルに乗算をマッピングし、結果を合計します。スカラー倍数の場合、元のポイントを取得し、スカラーによる乗算をそれらのポイントにマッピングし、それらを使用して新しいベクトルを作成します。


最後に、すべてをまとめます。

```coconut

data vector(*pts):

    """Immutable n-vector."""

    def __new__(cls, *pts):

        """Create a new vector from the given pts."""

        match [v `isinstance` vector] in pts:

            return v  # vector(v) where v is a vector should return v

        else:

            return pts |*> makedata$(cls)  # accesses base constructor

    def __abs__(self) =

        """Return the magnitude of the vector."""

        self.pts |> map$(.**2) |> sum |> (.**0.5)

    def __add__(self, vector(*other_pts)

                if len(other_pts) == len(self.pts)) =

        """Add two vectors together."""

        map((+), self.pts, other_pts) |*> vector

    def __sub__(self, vector(*other_pts)

                if len(other_pts) == len(self.pts)) =

        """Subtract one vector from another."""

        map((-), self.pts, other_pts) |*> vector

    def __neg__(self) =

        """Retrieve the negative of the vector."""

        self.pts |> map$(-) |*> vector

    def __mul__(self, other):

        """Scalar multiplication and dot product."""

        match vector(*other_pts) in other:

            assert len(other_pts) == len(self.pts)

            return map((*), self.pts, other_pts) |> sum  # dot product

        else:

            return self.pts |> map$(.*other) |*> vector  # scalar multiplication

    def __rmul__(self, other) =

        """Necessary to make scalar multiplication commutative."""

        self * other


# Test cases:

vector(1, 2, 3) |> print  # vector(*pts=(1, 2, 3))

vector(4, 5) |> vector |> print  # vector(*pts=(4, 5))

vector(3, 4) |> abs |> print  # 5

vector(1, 2) + vector(2, 3) |> print  # vector(*pts=(3, 5))

vector(2, 2) - vector(0, 1) |> print  # vector(*pts=(2, 1))

-vector(1, 3) |> print  # vector(*pts=(-1, -3))

(vector(1, 2) == "string") |> print  # False

(vector(1, 2) == vector(3, 4)) |> print  # False

(vector(2, 4) == vector(2, 4)) |> print  # True

2*vector(1, 2) |> print  # vector(*pts=(2, 4))

vector(1, 2) * vector(1, 3) |> print  # 7

```

コピーして貼り付けます。これは大量のコードです。しかし、よく見ると、すっきりしていて読みやすく、簡潔で、まさに私たちが意図したとおりに機能します。つまり、基本的なベクトル演算をサポートする不変の n ベクトルの代数データ型を作成するのです。そして、私たちは状態やループなどの命令型構造を必要とせずにすべてを実現しました。つまり、純粋な関数型プログラミングです。

## Case Study 4: `vector_field`


For the final case study, instead of me writing the code, and you looking at it, you'll be writing the code—of course, I won't be looking at it, but I will show you how I would have done it after you give it a shot by yourself.


_The bonus challenge for this section is to write each of the functions we'll be defining in just one line. Try using assignment functions to help with that!_


First, let's introduce the general goal of this case study. We want to write a program that will allow us to produce infinite vector fields that we can iterate over and apply operations to. And in our case, we'll say we only care about vectors with positive components.


Our first step, therefore, is going to be creating a field of all the points with positive `x` and `y` values—that is, the first quadrant of the `x-y` plane, which looks something like this:


## ケーススタディ4:

`vector_field`

最後のケーススタディでは、私がコードを書いてあなたがそれを見るのではなく、あなたがコードを書くことになります。もちろん、私がコードを見ることはありませんが、あなたが自分で試してみてから、私がどのように書いたかをお見せします。


_このセクションのボーナスチャレンジは、定義する各関数を 1 行で書くことです。そのためには、代入関数を使ってみてください!_


まず、このケーススタディの一般的な目標を紹介しましょう。反復処理して操作を適用できる無限のベクトルフィールドを作成できるプログラムを作成します。この場合、正の成分を持つベクトルのみを対象とすることにします。


したがって、最初のステップは、正の成分を持つすべてのポイントのフィールドを作成することです。

`x`

そして

`y`

価値観、つまり第一象限

`x-y`

飛行機は次のようになります。

```

...


(0,2)   ...


(0,1)   (1,1)   ...


(0,0)   (1,0)   (2,0)   ...

```



But since we want to be able to iterate over that plane, we're going to need to linearize it somehow, and the easiest way to do that is to split it up into diagonals, and traverse the first diagonal, then the second diagonal, and so on, like this:


しかし、その平面を反復処理できるようにしたいので、何らかの方法でそれを線形化する必要があります。そのための最も簡単な方法は、それを対角線に分割し、次のように最初の対角線、次に 2 番目の対角線、というように走査することです。

```

(0, 0), (1, 0), (0, 1), (2, 0), (1, 1), (0, 2), ...

```

## `diagonal_line`


Thus, our first function `diagonal_line(n)` should construct an iterator of all the points, represented as coordinate tuples, in the `n`th diagonal, starting with `(0, 0)` as the `0`th diagonal. Like we said at the start of this case study, this is where we I let go and you take over. Using all the tools of functional programming that Coconut provides, give `diagonal_line` a shot. When you're ready to move on, scroll down.


Here are some tests that you can use:


`diagonal_line`

したがって、最初の関数は

`diagonal_line(n)`

座標タプルとして表されるすべての点の反復子を構築する必要があります。

`n`

対角線で始まり、

`(0, 0)`

として

`0`

対角線です。このケーススタディの冒頭で述べたように、ここでは私が手放し、あなたが引き継ぎます。Coconutが提供する関数型プログラミングのすべてのツールを使って、

`diagonal_line`

試してみましょう。次に進む準備ができたら、下にスクロールしてください。


使用できるテストをいくつか紹介します。

```coconut

diagonal_line(0) `isinstance` (list, tuple) |> print  # False (should be an iterator)

diagonal_line(0) |> list |> print  # [(0, 0)]

diagonal_line(1) |> list |> print  # [(0, 1), (1, 0)]

```



_Hint: the `n`th diagonal should contain `n+1` elements, so try starting with `range(n+1)` and then transforming it in some way._


<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>


That wasn't so bad, now was it? Now, let's take a look at my solution:


_ヒント:

`n`

対角線には

`n+1`

要素があるので、まずは

`range(n+1)`

そして、それを何らかの方法で変換します。_


<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>


それほど悪くはなかったでしょう? では、私の解決策を見てみましょう:

```coconut

def diagonal_line(n) = range(n+1) |> map$(i => (i, n-i))

```

かなりシンプルですよね?

`range(n+1)`

、そして使用する

`map`

正しいタプルのシーケンスに変換します。


#

## `linearized_plane`


Now that we've created our diagonal lines, we need to join them together to make the full linearized plane, and to do that we're going to write the function `linearized_plane()`. `linearized_plane` should produce an iterator that goes through all the points in the plane, in order of all the points in the first `diagonal(0)`, then the second `diagonal(1)`, and so on. `linearized_plane` is going to be, by necessity, an infinite iterator, since it needs to loop through all the points in the plane, which have no end. To help you accomplish this, remember that the `::` operator is lazy, and won't evaluate its operands until they're needed, which means it can be used to construct infinite iterators. When you're ready to move on, scroll down.


Tests:


`linearized_plane`

対角線ができたので、それらを結合して完全な線形平面を作成する必要があります。そのためには、関数を記述します。

`linearized_plane()`

`linearized_plane`

平面上のすべての点を、最初の点の順に調べる反復子を生成する必要がある。

`diagonal(0)`

、次に2番目

`diagonal(1)`

、 等々。

`linearized_plane`

は、平面上のすべての点をループする必要があるため、必然的に無限反復子になります。この操作を実行するには、

`::`

演算子は遅延型であり、必要になるまでオペランドを評価しません。つまり、無限イテレータの構築に使用できます。先に進む準備ができたら、下にスクロールしてください。


テスト:

```coconut

# Note: these tests use $[] notation, which we haven't introduced yet

#  but will introduce later in this case study; for now, just run the

#  tests, and make sure you get the same result as is in the comment

linearized_plane()$[0] |> print  # (0, 0)

linearized_plane()$[:3] |> list |> print  # [(0, 0), (0, 1), (1, 0)]

```



_Hint: instead of defining the function as `linearized_plane()`, try defining it as `linearized_plane(n=0)`, where `n` is the diagonal to start at, and use recursion to build up from there._


<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>


That was a little bit rougher than the first one, but hopefully still not too bad. Let's compare to my solution:


_ヒント: 関数を次のように定義する代わりに

`linearized_plane()`

次のように定義してみましょう

`linearized_plane(n=0)`

、 どこ

`n`

は開始する対角線であり、そこから再帰を使用して構築します。_


<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>


これは最初のものより少し荒いですが、それでもそれほど悪くはないと思います。私の解決策と比較してみましょう。

```coconut

def linearized_plane(n=0) = diagonal_line(n) :: linearized_plane(n+1)

```

ご覧のとおり、これは非常に基本的な単純な解決策です。

`::`

そして、すべての対角線を順番に結合するための再帰。


#

## `vector_field`


Now that we have a function that builds up all the points we need, it's time to turn them into vectors, and to do that we'll define the new function `vector_field()`, which should turn all the tuples in `linearized_plane` into vectors, using the n-vector class we defined earlier.


Tests:


`vector_field`

必要な点をすべて集める関数ができたので、次はそれをベクトルに変換します。そのためには、新しい関数を定義します。

`vector_field()`

、これはすべてのタプルを

`linearized_plane`

先ほど定義した n ベクトル クラスを使用して、ベクトルに変換します。


テスト:

```coconut

# You'll need to bring in the vector class from earlier to make these work

vector_field()$[0] |> print  # vector(*pts=(0, 0))

vector_field()$[2:3] |> list |> print  # [vector(*pts=(1, 0))]

```



_Hint: Remember, the way we defined vector it takes the components as separate arguments, not a single tuple. You may find the [Coconut built-in `starmap`](./DOCS.md#starmap) useful in dealing with that._


<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>


We're making good progress! Before we move on, check your solution against mine:


_ヒント: 覚えておいてください、ベクトルの定義方法は、要素を単一のタプルではなく、別々の引数として受け取ります。[Coconut組み込み

`starmap`

](./DOCS.md#starmap) は、その問題に対処するのに役立ちます。_


<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>


順調に進んでいます。先に進む前に、あなたの解決策と私の解決策を比較してください。

```coconut

def vector_field() = linearized_plane() |> starmap$(vector)

```

私たちがやっているのは、

`linearized_plane`

およびマッピング

`vector`

それを超えて、しかし使用して

`starmap`

の代わりに

`map`

となることによって

`vector`

タプルの各要素を個別の引数として呼び出されます。


#

## Applications


Now that we've built all the functions we need for our vector field, it's time to put it all together and test it. Feel free to substitute in your versions of the functions below:


## アプリケーション


これで、ベクトル フィールドに必要なすべての関数を構築できました。次は、すべてをまとめてテストします。以下の関数を自由に置き換えてください。

```coconut

data vector(*pts):

    """Immutable n-vector."""

    def __new__(cls, *pts):

        """Create a new vector from the given pts."""

        match [v `isinstance` vector] in pts:

            return v  # vector(v) where v is a vector should return v

        else:

            return pts |*> makedata$(cls)  # accesses base constructor

    def __abs__(self) =

        """Return the magnitude of the vector."""

        self.pts |> map$(.**2) |> sum |> (.**0.5)

    def __add__(self, vector(*other_pts)

                if len(other_pts) == len(self.pts)) =

        """Add two vectors together."""

        map((+), self.pts, other_pts) |*> vector

    def __sub__(self, vector(*other_pts)

                if len(other_pts) == len(self.pts)) =

        """Subtract one vector from another."""

        map((-), self.pts, other_pts) |*> vector

    def __neg__(self) =

        """Retrieve the negative of the vector."""

        self.pts |> map$(-) |*> vector

    def __mul__(self, other):

        """Scalar multiplication and dot product."""

        match vector(*other_pts) in other:

            assert len(other_pts) == len(self.pts)

            return map((*), self.pts, other_pts) |> sum  # dot product

        else:

            return self.pts |> map$(.*other) |*> vector  # scalar multiplication

    def __rmul__(self, other) =

        """Necessary to make scalar multiplication commutative."""

        self * other


def diagonal_line(n) = range(n+1) |> map$(i => (i, n-i))

def linearized_plane(n=0) = diagonal_line(n) :: linearized_plane(n+1)

def vector_field() = linearized_plane() |> starmap$(vector)


# Test cases:

diagonal_line(0) `isinstance` (list, tuple) |> print  # False (should be an iterator)

diagonal_line(0) |> list |> print  # [(0, 0)]

diagonal_line(1) |> list |> print  # [(0, 1), (1, 0)]

linearized_plane()$[0] |> print  # (0, 0)

linearized_plane()$[:3] |> list |> print  # [(0, 0), (0, 1), (1, 0)]

vector_field()$[0] |> print  # vector(*pts=(0, 0))

vector_field()$[2:3] |> list |> print  # [vector(*pts=(1, 0))]

```

コピーして貼り付けます。独自の関数に置き換えた場合、すべてが正しく動作することを確認したら、最後の 4 つのテストを確認してください。先ほど見た部分適用の表記法に似ていますが、括弧の代わりに角括弧を使用する新しい表記法が使用されていることに気づくでしょう。これはイテレータスライスの表記法です。部分適用が遅延関数呼び出しであったのと同様に、イテレータ スライスは _遅延シーケンス スライス_ です。部分適用と同様に、

`$`

_lazy-ify_ 演算子として、この場合は、すぐに評価される通常の Python スライスを、スライス内の要素が必要な場合にのみ評価される遅延イテレータ スライスに変換します。それを念頭に置いて、ベクトル フィールドを構築したので、今度はイテレータ スライスを使用して操作してみましょう。ベクトルフィールドで何かクールなことをしてみましょう

- 作成する

`magnitude_field`

各点はベクトルの大きさです

- ベクトル場全体を結合して

`map`

そして、先ほど書いたベクトルの加算と乗算のメソッド


次に、イテレータ スライスを使用して部分を取り出し、調べます。

## ケーススタディ5:

`vector`

パート II


いくつかのアプリケーションでは、

`vector_field`

なぜなら、私たちの

`vector`

。このケーススタディでは、特に次の 1 つに焦点を当てます。

`.angle`

`.angle`

1 つの引数と別のベクトルを受け取り、2 つのベクトル間の角度を計算します。数学的には、2 つのベクトル間の角度の式は、ベクトルのそれぞれの単位ベクトルの内積です。したがって、実装する前に

`.angle`

、私たちは必要になるでしょう

`.unit`

。数学的には、与えられたベクトルの単位ベクトルの式は、そのベクトルをその大きさで割ったものになります。したがって、実装する前に

`.unit`

、そして拡張して

`.angle`

、まずは除算を実装する必要があります。


#

## `__truediv__`


Vector division is just scalar division, so we're going to write a `__truediv__` method that takes `self` as the first argument and `other` as the second argument, and returns a new vector the same size as `self` with every element divided by `other`. For an extra challenge, try writing this one in one line using assignment function notation.


Tests:


`__truediv__`

ベクトルの割り算はスカラーの割り算なので、次のように書きます。

`__truediv__`

方法

`self`

最初の引数として

`other`

2番目の引数として、同じサイズの新しいベクトルを返します。

`self`

すべての要素を

`other`

追加のチャレンジとして、代入関数表記法を使用してこれを 1 行で記述してみてください。


テスト:

```coconut

vector(3, 4) / 1 |> print  # vector(*pts=(3.0, 4.0))

vector(2, 4) / 2 |> print  # vector(*pts=(1.0, 2.0))

```



_Hint: Look back at how we implemented scalar multiplication._


<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>


Here's my solution for you to check against:


_ヒント: スカラー乗算の実装方法を振り返ってください。_


<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>


確認するための私の解決策は次のとおりです。

```coconut

    def __truediv__(self, other) = self.pts |> map$(x => x/other) |*> vector

```

## `.unit`


Next up, `.unit`. We're going to write a `unit` method that takes just `self` as its argument and returns a new vector the same size as `self` with each element divided by the magnitude of `self`, which we can retrieve with `abs`. This should be a very simple one-line function.


Tests:


`.unit`

次は、

`.unit`

. これから書きます

`unit`

たったの

`self`

を引数として受け取り、同じサイズの新しいベクトルを返す。

`self`

各要素を大きさで割った

`self`

で取得できます。

`abs`

これは非常にシンプルな 1 行の関数です。


テスト:

```coconut

vector(0, 1).unit() |> print  # vector(*pts=(0.0, 1.0))

vector(5, 0).unit() |> print  # vector(*pts=(1.0, 0.0))

```



<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>


Here's my solution:


<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>


私の解決策は次のとおりです:

```coconut

    def unit(self) = self / abs(self)

```

## `.angle`


This one is going to be a little bit more complicated. For starters, the mathematical formula for the angle between two vectors is the `math.acos` of the dot product of those vectors' respective unit vectors, and recall that we already implemented the dot product of two vectors when we wrote `__mul__`. So, `.angle` should take `self` as the first argument and `other` as the second argument, and if `other` is a vector, use that formula to compute the angle between `self` and `other`, or if `other` is not a vector, `.angle` should raise a `MatchError`. To accomplish this, we're going to want to use destructuring assignment to check that `other` is indeed a `vector`.


Tests:


`.angle`

これは少し複雑になります。まず、2つのベクトル間の角度の数式は次のようになります。

`math.acos`

これらのベクトルのそれぞれの単位ベクトルのドット積を計算し、2つのベクトルのドット積を、次のように書いたときにすでに実装していたことを思い出してください。

`__mul__`

。 それで、

`.angle`

取るべきです

`self`

最初の引数として

`other`

2番目の引数として、そして

`other`

はベクトルなので、その式を使って角度を計算します。

`self`

そして

`other`

、または

`other`

ベクトルではない、

`.angle`

上げるべきだ

`MatchError`

これを実現するには、分割代入を使用して次のことをチェックします。

`other`

確かに

`vector`

.


テスト:

```coconut

import math

vector(2, 0).angle(vector(3, 0)) |> print  # 0.0

print(vector(1, 0).angle(vector(0, 2)), math.pi/2)  # should be the same

vector(1, 2).angle(5)  # MatchError

```



_Hint: Look back at how we checked whether the argument to `factorial` was an integer using pattern-matching._


<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>


Here's my solution—take a look:


_ヒント: 引数が

`factorial`

パターンマッチングを使用した整数でした。_


<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>

<br>


私の解決策は次のとおりです。ご覧ください。

```coconut

    def angle(self, other `isinstance` vector) = math.acos(self.unit() * other.unit())

```



And now it's time to put it all together. Feel free to substitute in your own versions of the methods we just defined.



さて、すべてをまとめる時が来ました。今定義したメソッドを自由に独自のバージョンに置き換えてください。

```coconut

import math  # necessary for math.acos in .angle


data vector(*pts):

    """Immutable n-vector."""

    def __new__(cls, *pts):

        """Create a new vector from the given pts."""

        match [v `isinstance` vector] in pts:

            return v  # vector(v) where v is a vector should return v

        else:

            return pts |*> makedata$(cls)  # accesses base constructor

    def __abs__(self) =

        """Return the magnitude of the vector."""

        self.pts |> map$(.**2) |> sum |> (.**0.5)

    def __add__(self, vector(*other_pts)

                if len(other_pts) == len(self.pts)) =

        """Add two vectors together."""

        map((+), self.pts, other_pts) |*> vector

    def __sub__(self, vector(*other_pts)

                if len(other_pts) == len(self.pts)) =

        """Subtract one vector from another."""

        map((-), self.pts, other_pts) |*> vector

    def __neg__(self) =

        """Retrieve the negative of the vector."""

        self.pts |> map$(-) |*> vector

    def __mul__(self, other):

        """Scalar multiplication and dot product."""

        match vector(*other_pts) in other:

            assert len(other_pts) == len(self.pts)

            return map((*), self.pts, other_pts) |> sum  # dot product

        else:

            return self.pts |> map$(.*other) |*> vector  # scalar multiplication

    def __rmul__(self, other) =

        """Necessary to make scalar multiplication commutative."""

        self * other

    # New one-line functions necessary for finding the angle between vectors:

    def __truediv__(self, other) = self.pts |> map$(x => x/other) |*> vector

    def unit(self) = self / abs(self)

    def angle(self, other `isinstance` vector) = math.acos(self.unit() * other.unit())


# Test cases:

vector(3, 4) / 1 |> print  # vector(*pts=(3.0, 4.0))

vector(2, 4) / 2 |> print  # vector(*pts=(1.0, 2.0))

vector(0, 1).unit() |> print  # vector(*pts=(0.0, 1.0))

vector(5, 0).unit() |> print  # vector(*pts=(1.0, 0.0))

vector(2, 0).angle(vector(3, 0)) |> print  # 0.0

print(vector(1, 0).angle(vector(0, 2)), math.pi/2)  # should be the same

vector(1, 2).angle(5)  # MatchError

```

_ここで注意すべき点が1つあります。メソッドを置換するときに空白行を残さないように注意してください。そうしないと、インタープリタがコードを切り捨ててしまいます。

`vector`

そこには。これは通常の Coconut コードでは問題になりませんが、ここではコマンド ラインにコピー アンド ペーストしているために問題が発生します。_


コピーして貼り付けてください。すべてがうまくいけば、もう一度試してみましょう。

`vector_field`

新しいメソッドを使用した[アプリケーション](#applications)。

## ギャップを埋める


これで、このチュートリアルではケース スタディは終わりですが、Coconut の機能がすべてではないというわけではありません。この最後のセクションでは、ケース スタディで取り上げなかった Coconut のその他の便利な機能について触れます。


#

## Lazy Lists


First up is lazy lists. Lazy lists are lazily-evaluated lists, similar in their laziness to Coconut's `::` operator, in that any expressions put inside a lazy list won't be evaluated until that element of the lazy list is needed. The syntax for lazy lists is exactly the same as the syntax for normal lists, but with "banana brackets" (`(|` and `|)`) instead of normal brackets, like so:


## 遅延リスト


まずは遅延リストです。遅延リストは遅延評価されるリストで、Coconut の遅延リストと似ています。

`::`

演算子は、遅延リスト内の式は、遅延リストの要素が必要になるまで評価されないという点で、遅延リストの構文は通常のリストとまったく同じですが、「バナナ括弧」 (

`(|`

そして

`|)`

) を通常の括弧の代わりに使用します。次のようになります。

```coconut

abc = (| a, b, c |)

```



Unlike Python iterators, lazy lists can be iterated over multiple times and still return the same result.


Unlike Python lists, however, using a lazy list, it is possible to define the values used in the following expressions as needed without raising a `NameError`:



Python のイテレータとは異なり、遅延リストは複数回反復しても同じ結果を返すことができます。


ただし、Python のリストとは異なり、遅延リストを使用すると、例外を発生させることなく、必要に応じて次の式で使用される値を定義できます。

`NameError`

```coconut

abcd = (| d(a), d(b), d(c) |)  # a, b, c, and d are not defined yet

def d(n) = n + 1


a = 1

abcd$[0]

b = 2

abcd$[1]

c = 3

abcd$[2]

```

## Function Composition


Next is function composition. In Coconut, this is primarily accomplished through the `f1 ..> f2` operator, which takes two functions and composes them, creating a new function equivalent to `(*args, **kwargs) => f2(f1(*args, **kwargs))`. This can be useful in combination with partial application for piecing together multiple higher-order functions, like so:


## 関数合成


次は関数合成です。Coconut では、これは主に

`f1 ..> f2`

演算子は2つの関数を合成し、次の関数と同等の新しい関数を作成します。

`(*args, **kwargs) => f2(f1(*args, **kwargs))`

これは、次のように複数の高階関数を組み立てるために部分適用と組み合わせると便利です。

```coconut

zipsum = zip ..> map$(sum)

```



_While `..>` is generally preferred, if you'd rather use the more traditional mathematical function composition ordering, you can get that with the `<..` operator._


If the composed functions are wrapped in parentheses, arguments can be passed into them:


_その間

`..>`

一般的には、より伝統的な数学関数の合成順序が好まれますが、より伝統的な数学関数の合成順序を使用したい場合は、

`<..`

演算子._


合成された関数が括弧で囲まれている場合は、引数を渡すことが可能です。

```coconut

def plus1(x) = x + 1

def square(x) = x * x


(square ..> plus1)(3) == 10  # True

```



Functions of different arities can be composed together, as long as they are in the correct order. If they are in the incorrect order, a `TypeError` will be raised. In this example we will compose a unary function with a binary function:


異なる引数の関数は、正しい順序であれば組み合わせることができます。順序が間違っていると、

`TypeError`

が発生します。この例では、単項関数と二項関数を組み合わせます。

```coconut

def add(n, m) = n + m  # binary function

def square(n) = n * n  # unary function


(square ..> add)(3, 1)    # Raises TypeError: square() takes exactly 1 argument (2 given)

(add ..> square)(3, 1)    # 16

```



Another useful trick with function composition involves composing a function with a higher-order function:


関数合成のもう 1 つの便利なトリックは、関数を高階関数と合成することです。

```coconut

def inc_or_dec(t):

    # Our higher-order function, which returns another function

    if t:

        return x => x+1

    else:

        return x => x-1


def square(n) = n * n


square_inc = inc_or_dec(True) ..> square

square_dec = inc_or_dec(False) ..> square

square_inc(4)  # 25

square_dec(4)  # 9


```

_注: Coconutは関数合成演算子もサポートしています

`..`

`..*>`

`<*..`

`..**>`

、 そして

`<**..`

._


#

## Implicit Partials


Another useful Coconut feature is implicit partials. Coconut supports a number of different "incomplete" expressions that will evaluate to a function that takes in the part necessary to complete them, that is, an implicit partial application function. The different allowable expressions are:


## 暗黙的な部分関数


もう 1 つの便利な Coconut 機能は、暗黙的な部分関数です。Coconut は、さまざまな「不完全な」式をサポートしています。これらの式は、式を完成させるために必要な部分を取り込む関数、つまり暗黙的な部分適用関数として評価されます。使用できるさまざまな式は次のとおりです。

```coconut

.attr

.method(args)

func$

seq[]

iter$[]

.[slice]

.$[slice]

```

各暗黙的部分関数の機能の詳細については、Coconut の [暗黙的部分関数](./DOCS.md#implicit-partial-application) に関するドキュメントを参照してください。


#

## Type Annotations


For many people, one of the big downsides of Python is the fact that it is dynamically-typed. In Python, this problem is addressed by [MyPy](http://mypy-lang.org/), a static type analyzer for Python, which can check Python-3-style type annotations such as


## 型注釈


多くの人にとって、Python の大きな欠点の 1 つは、動的な型付けであることです。Python では、この問題は [MyPy](http://mypy-lang.org/) によって解決されています。これは Python 用の静的型アナライザーで、次のような Python 3 スタイルの型注釈をチェックできます。

```coconut_python

def plus1(x: int) -> int:

    return x + 1

a: int = plus1(10)

```



Unfortunately, in Python, such type annotation syntax only exists in Python 3. Not to worry in Coconut, however, which compiles Python-3-style type annotations to universally compatible type comments. Not only that, but Coconut has built-in [MyPy integration](./DOCS.md#mypy-integration) for automatically type-checking your code, and its own [enhanced type annotation syntax](./DOCS.md#enhanced-type-annotation) for more easily expressing complex types, like so:


残念ながら、Python では、このような型注釈構文は Python 3 にしか存在しません。しかし、Coconut では心配無用です。Coconut は Python 3 スタイルの型注釈を、普遍的に互換性のある型コメントにコンパイルします。それだけでなく、Coconut には、コードの型チェックを自動的に行うための [MyPy 統合](./DOCS.md#mypy-integration) が組み込まれており、複雑な型をより簡単に表現するための独自の [拡張型注釈構文](./DOCS.md#enhanced-type-annotation) も用意されています。

```coconut

def int_map(

    f: int -> int,

    xs: int[],

) -> int[] =

    xs |> map$(f) |> list

```

## 参考資料


このチュートリアルはこれで終わりです。しかし、Coconut についてはこれで終わりではありません。このチュートリアルで取り上げたすべての機能、および他の多くの機能は、Coconut の [ドキュメント](./DOCS.md) で詳しく説明されています。


また、このチュートリアルで取り上げられていない他の質問がある場合は、Coconut 開発者向けの GitHub 統合チャット ルームである Coconut の [Gitter](https://gitter.im/evhub/coconut) でお気軽に質問してください。


最後に、Coconut は新しい成長中の言語です。Coconut の開発に参加したい場合は、すべてのコードが Coconut の [GitHub](https://github.com/evhub/coconut) で完全にオープン ソースで利用できます。貢献は、コードをフォークし、変更を加えて、プル リクエストを提案するだけです。詳細については、Coconut の [貢献ガイドライン](./CONTRIBUTING.md) を参照してください。

いいなと思ったら応援しよう!