Python で関数型プログラミング(っぽいもの)に入門する
はじめに
最近、 exercism.io を始めたのですが、Pythonでちょっと面白い問題に遭遇しました。Pythonで関数型プログラミングをやってみろという課題です。この課題を通じて関数型プログラミングに入門するための基本的な考え方を解説を試みます。なお、筆者は、Pythonも関数型プログラミングもずぶの素人です。
なお、ここでは、Python 3.7.0 を動作確認に使ってます。
課題
(全部、そのまま掲載するのはどうかと思うので、代表して1つだけ記載します。)
Python の既存の関数を使わずにリストの基本操作の1つ append
関数を実装してください。
append([], []) # => []
append([], [1, 2, 3, 4]) # => [1, 2, 3, 4]
append([1, 2], [2, 3, 4, 5]) # => [1, 2, 2, 3, 4, 5]
関数型プログラミング(っぽいもの)の基本的な考え方
関数型プログラミングでは、リスト(配列)を扱うのは、基本中の基本です。リストの先頭の値を求める、リストの先頭を除いた残りを求める、リストの先頭に1つ値を追加して新たなリストを作る、などです。こういった基本的な操作を組み合わせて、リストに対する関数を実装していくことになります。
大体、関数型プログラミングを勉強すると最初の方で、基本的な操作が登場してきます。(Scheme だと car, cdr, cons などですね。)
Pythonでリストの先頭を取り出す
Pythonのリスト list
の先頭を取り出すやり方はいろいろあるでしょうが、ここでは、以下のやり方を使います。
head, *tail = list
ここで、head には、listの先頭が入ります。
>>> list = [1, 2, 3]
>>> head, *tail = list
>>> head
1
>>>
list[0]
とやってもいいのでしょうが、関数型プログラミング言語 Elixir を少しだけ触ったことがある筆者はこちらの方が好みです。
ちなみに、Elixir だとこんな感じです。
[head|tail] = list
あと、Pythonでは、リストが空のときは、ValueErrorになるので注意しましょう。
>>> list = []
>>> head, *tail = list
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: not enough values to unpack (expected at least 1, got 0)
Python でリストの先頭を除いた残りを求める
もう既に登場してますね。
head, *tail = list
tail
が先頭を除いた残りになります。
>>> list = [1, 2, 3]
>>> head, *tail = list
>>> tail
[2, 3]
Python でリストの先頭に1つ値を追加して新たなリストを作る
head が1つの値、tailがリストだとすると、tail に head を先頭につけたリストは以下のようにするとできます。
list = [head, *tail]
実際の例はこんな感じです。
>>> head = 1
>>> tail = [2, 3]
>>> list = [head, *tail]
>>> list
[1, 2, 3]
Python でリストの末尾に1つ値を追加して新たなリストを作る
Python では、同じ要領で末尾にひとつ要素を追加して新たなリストを作ることもできます。headがリストで、tailが1つの要素だとすると
list = [*head, tail]
とできます。実際の例はこんな感じ。
>>> head = [1, 2]
>>> tail = 3
>>> list = [*head, tail]
>>> list
[1, 2, 3]
append の実装
これまでの操作を組み合わせるとこんな感じで append を実装することができます。
def append(xs, ys):
while ys != []:
head, *ys = ys
xs = [*xs, head]
return xs
以下のように再帰呼出しにする方が関数型プログラミングっぽいのですが、リストの要素の数が多くなると、RecursionError になるので、避けた方が良いでしょう。
def append(xs, ys):
if ys == []:
return xs
head, *ys = ys
return append([*xs, head], ys)
まとめ
Pythonで関数型プログラミング(っぽいもの)に入門してみました。同様にして、map関数、filter関数、foldl関数、foldr関数なども実装することができます。
なお、私が実装したコードに対して、mentor さんからのコメントが、まだついてません。ですので、これが正解かどうかはわかりません
おまけ
試してないですが、同じようなことは、Ruby でもできるはずです(社名が社名なので、やっぱりRubyを無理矢理にでも絡めておかないと )
$ irb
irb(main):001:0> list = [1,2,3]
=> [1, 2, 3]
irb(main):002:0> head, *tail = list
=> [1, 2, 3]
irb(main):003:0> head
=> 1
irb(main):004:0> tail
=> [2, 3]
irb(main):005:0> [head, *tail]
=> [1, 2, 3]
irb(main):006:0> [*tail, head]
=> [2, 3, 1]
irb(main):007:0>
追記 (2018-09-13)
その後、メンターさんから well done
とコメントもらいました。ただ、アドバイスとして、
while ys != []:
は
while ys:
と書いた方が速いと言われました。 []
が false になるんですね。
>>> a = []
>>> bool(a)
False
>>> a = [1]
>>> bool(a)
True
Ruby は(確か Elixir も) nil と false だけ false なので、その癖が出てしまいました
Author And Source
この問題について(Python で関数型プログラミング(っぽいもの)に入門する), 我々は、より多くの情報をここで見つけました https://qiita.com/suketa/items/1a04f82bba11c7646172著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .