 プログラミング言語
プログラミング言語コースを見て、 
関数型JavaScriptを読んだ後、Pythonでこれらすべてのクールなことを繰り返したいと
思いました。 いくつかのことは美しく簡単に行われ、残りは怖くて使えないことが判明しました。
記事には以下が含まれます。
- いくつかの不可解な言葉;
- カレー;
- パターンマッチング;
- 再帰(末尾を含む)。
この記事は、Python 3.3以降向けに設計されています。 
紛らわしい言葉
匿名関数があるので、関数スタイルでpythonで書くことができます:
sum_x_y = lambda x, y: x + y print(sum_x_y(1, 2))  
高階関数(他の関数を受け入れるか返す):
 def call_and_twice(fnc, x, y): return fnc(x, y) * 2 print(call_and_twice(sum_x_y, 3, 4))  
短絡:
 def closure_sum(x): fnc = lambda y: x + y return fnc sum_with_3 = closure_sum(3) print(sum_with_3(12))  
タプルのアンパック(ほぼパターンマッチング):
 a, b, c = [1, 2, 3] print(a, b, c)  
そして、モジュール
functoolsと
itertoolsをクールにします。
カレー
関数を多くの引数から一度に1つの引数を取る関数に変換します。
最も単純な場合を考えて、sum_x_y関数をカリー化します。
 sum_x_y_carry = lambda x: lambda y: sum_x_y(x, y) print(sum_x_y_carry(5)(12))  
何かがまったくクールではない、これを試してください:
 sum_with_12 = sum_x_y_carry(12) print(sum_with_12(1), sum_with_12(12))  
すでにもっと興味深いので、 
lambda x: lambda y: zzzz書くたびにクールではないので、2つの引数を持つ関数をカリー化するユニバーサル関数を作成します。
 curry_2 = lambda fn: lambda x: lambda y: fn(x, y) 
そして、それを実際のプロジェクトで使用される
map関数に適用します。
 curry_map_2 = curry_2(map) @curry_map_2 def twice_or_increase(n): if n % 2 == 0: n += 1 if n % 3: n *= 2 return n print(*twice_or_increase(range(10)))  
はい、はい、カリー
mapれた
mapをデコレーターとして使用し、複数行のラムダの欠如を相殺しました。
ただし、すべての関数が2つの引数を取るわけではないため、 
partial 、 
curry_n 、および少しの再帰を使用して
curry_n関数を作成します。
 from functools import partial def curry_n(fn, n): def aux(x, n=None, args=None):  
そしてもう一度、 
mapに適用し
mapが、引数は3つです。
 curry_3_map = curry_n(map, 3) 
そして、リストアイテムをリストアイテム1..10に追加する関数を作成します。
 sum_arrays = curry_3_map(lambda x, y: x + y) sum_with_range_10 = sum_arrays(range(10)) print(*sum_with_range_10(range(100, 0, -10)))  
curry_2は
curry_2の特殊なケースなので、次のことができます。
 curry_2 = partial(curry_n, n=2) 
そして、例えば、それを
filterに適用し
filter :
 curry_filter = curry_2(filter) only_odd = curry_filter(lambda n: n % 2) print(*only_odd(range(10)))  
パターンマッチング
指定されたサンプルの存在についてリストまたはその他のデータ構造を分析する方法。
パターンマッチングは、私がsmlで最も気に入っており、Pythonで最も悪いことです。
私たちは目標を思いつきます-次のような関数を書くことです:
- 数字のリストを受け入れる場合、その製品を返します。
- 文字列のリストを受け入れる場合、1つの大きな連結文字列を返します。
補助的な例外とその「スロー」のための関数を作成しましょう。比較がパスしない場合に使用します。
 class NotMatch(Exception): """Not match""" def not_match(x): raise NotMatch(x) 
そして、チェックを行ってオブジェクトを返す、または例外をスローする関数:
 match = lambda check, obj: obj if check(obj) else not_match(obj) match_curry = curry_n(match, 2) 
これで型チェックを作成できます:
 instance_of = lambda type_: match_curry(lambda obj: isinstance(obj, type_)) 
次に、 
int :
 is_int = instance_of(int) print(is_int(2))  
リストのタイプチェックを作成し、各要素をチェックします。
 is_array_of = lambda matcher: match_curry(lambda obj: all(map(matcher, obj))) 
そして、 
int :
 is_array_of_int = is_array_of(is_int) print(is_array_of_int([1, 2, 3]))  
そして今、 
strについても同様です:
 is_str = instance_of(str) is_array_of_str = is_array_of(is_str) 
また、引数、べき等値=)を返す関数を追加します
 identity = lambda x: x print(identity(10))  
そして、空のリストを確認します。
 is_blank = match_curry(lambda xs: len(xs) == 0) print(is_blank([]))  
「チェック」を使用して、リストを最初の要素と残りの要素に分割する関数を作成します。
 def hd_tl(match_x, match_xs, arr): x, *xs = arr return match_x(x), match_xs(xs) hd_tl_partial = lambda match_x, match_xs: partial(hd_tl, match_x, match_xs) 
そして、 
identity持つ最も単純な例を考えてみましょう:
 hd_tl_identity = hd_tl_partial(identity, identity) print(hd_tl_identity(range(5)))  
そして今、数字で:
 hd_tl_ints = hd_tl_partial(is_int, is_array_of_int) print(hd_tl_ints(range(2, 6)))  
そして今、関数自体は、すべてのチェックを反復処理します。 彼女はとてもシンプルです:
 def pattern_match(patterns, args): for pattern, fnc in patterns: try: return fnc(pattern(args)) except NotMatch: continue raise NotMatch(args) pattern_match_curry = curry_n(pattern_match, 2) 

ただし、使用するのは不便であり、括弧の世界が必要です。たとえば、必要な関数は次のようになります。
 sum_or_multiply = pattern_match_curry(( (hd_tl_partial(identity, is_blank), lambda arr: arr[0]),  
動作を確認してください:
 print(sum_or_multiply(range(1, 10)))  
やった! 動作します=)
再帰
すべてのクールなプログラミング言語では、タフな人は再帰によって
mapを実装しますが、なぜ私たちは悪いのでしょうか? さらに、パターンマッチングの方法は既にわかっています。
 r_map = lambda fn, arg: pattern_match(( (hd_tl_partial(identity, is_blank), lambda arr: [fn(arr[0])]),  
今カレー:
 r_map_curry = curry_n(r_map, 2) twice = r_map_curry(lambda x: x * 2) print(twice(range(10)))  
何か問題が発生しました。末尾再帰を試してください。
これを行うには、 
None 「チェック」を作成します。
 is_none = match_curry(lambda obj: obj is None) 
そしてカップルチェック:
 pair = lambda match_x, match_y: lambda arr: (match_x(arr[0]), match_y(arr[1])) 
そして今、それ自体を
mapます:
 def r_map_tail(fn, arg): aux = lambda arg: pattern_match(( (pair(identity, is_none), lambda arr: aux([arr[0], []])),  
今、私たちの奇跡を試してください:
 r_map_tail_curry = curry_n(r_map_tail, 2) twice_tail = r_map_tail_curry(lambda x: x * 2) print(twice_tail(range(10)))  
それは不運です-Pythonは末尾再帰を最適化しません。 しかし、松葉杖は私たちの助けになります:
 def tail_fnc(fn): called = False calls = [] def run(): while len(calls):  
次に、この
mapを使用して実装し
map 。
 def r_map_really_tail(fn, arg): aux = tail_fnc(lambda arg: pattern_match((  
今ではうまくいきました=)
そんなに怖くない

 ひどいパターンマッチングを忘れると、再帰
mapを非常に正確に実装できます。
 def tail_r_map(fn, arr_): @tail_fnc def aux(arr, acc=None): x, *xs = arr if xs: return aux(xs, acc + [fn(x)]) else: return acc + [fn(x)] return aux(arr_, []) curry_tail_r_map = curry_2(tail_r_map) 
そして、リスト内のすべての奇数に2を掛けます。
 @curry_tail_r_map def twice_if_odd(x): if x % 2 == 0: return x * 2 else: return x print(twice_if_odd(range(10000))) # [0, 1, 4, 3, 8, 5, 12, 7, 16, 9, 20, 11, 24, 13, 28, 15, 32, 17, 36, 19... 
ゆっくりと不必要にではありますが、かなり正確に判明しました。 少なくとも速度のため。 さまざまな
mapオプションのパフォーマンスを比較します。
 from time import time checker = lambda x: x ** 2 + x limit = 10000 start = time() xs = [checker(x) for x in range(limit)][::-1] print('inline for:', time() - start) start = time() xs = list(map(checker, range(limit)))[::-1] print('map:', time() - start) calculate = curry_tail_r_map(checker) start = time() xs = calculate(range(limit))[::-1] print('r_map without pattern matching:', time() - start) calculate = r_map_really_tail_curry(checker) start = time() xs = calculate(range(limit))[::-1] print('r_map with pattern matching:', time() - start) 
その後、以下が得られます。
インライン: 0.011110067367553711
マップ: 0.011012554168701172
パターンマッチングなしのr_map: 3.7527310848236084
パターンマッチングを使用した r_map 
: 5.926968812942505
パターンマッチングオプションは最も低速であることが判明しましたが、ビルトインマップと結果は最速でした。
おわりに
この記事から、実際のアプリケーションでは、おそらくカレーのみを使用できます。 残りは読めないか、ブレーキバイク=)です。
すべての例はgithubで入手できます。