Python関数式プログラミング:入門から走火入魔まで

8139 ワード

Java/C/C++/機械学習/アルゴリズムとデータ構造/フロントエンド/Android/Python/プログラマー必読書籍リストを添付します.
本单ナビゲーションページ(右侧の极客侠桟をクリックして个人ブログを开く):极客侠桟①【Java】学习の道吐血整理技术书入门から进阶まで最全50+本(秘蔵版)②【アルゴリズムデータ构造+acm】入门から进阶吐血整理书単50+本(秘蔵版)③【データベース】入门から进阶まで必読18本技术书网盘吐血整理网盘(秘蔵版)④【Webフロントエンド】HTMLからJSまでAJAXからHTTPまでフレームから全桟までより少ないカーブ(秘蔵版)    ⑤【python】本はすべて整理済み(入門から進級まで)(秘蔵版)
⑥【機械学習】+python整理技術書(入門から進級まで整理済み)⑦【C言語】推薦書入門から進級まで大牛の道を案内する⑧【アンドロイド】入門から進級まで推薦書整理pdf書単整理(秘蔵版)
⑨【アーキテクチャ師】の路史詩級必読書単吐血整理四次元シリーズ80+本書(秘蔵版)
⑩【C++】吐血整理推薦書入門から進級成神への道100+本(秘蔵)
⑪【ios】IOSブック入門から段階的吐血整理(秘蔵版)
-------------------------------------------------------------------------------------------------------------------------------------------
関数式プログラミングは数学理論に由来し,数学計算に関連するシーンにもより適しているようであるため,本稿では簡単なデータ処理問題を例に,Python関数式プログラミングの入門から火事入魔までの過程を徐々に紹介する.
多くの人が関数式プログラミングについて話していますが、多くの人が異なる角度から見ているのは全く違う風景です.実用主義を堅持するPythonの古い司機たちはFPに対する態度をもっと包容すべきで、銀弾を信じませんが、冥冥冥の中でFPがPythonの教義に暗合しているような気がします(The Zen of Python)Pythonがマルチプログラミング言語であり、関数プログラミングを大きくサポートしている以上、拒否する理由はありません.
関数式プログラミングは数学理論に由来し,数学計算に関連するシーンにもより適しているようであるため,本稿では簡単なデータ処理問題を例に,Python関数式プログラミングの入門から火事入魔までの過程を徐々に紹介する.
質問:ある試験用紙のM道選択問題でのN人の学生の得点を計算する(問題ごとに点数が異なる).
まず、計算に使用される偽造データのセットを生成します.
# @file: data.py
import random
from collections import namedtuple

Student = namedtuple('Student', ['id', 'ans'])

N_Questions = 25
N_Students = 20

def gen_random_list(opts, n):
    return [random.choice(opts) for i in range(n)]

#      'ABCD'   
ANS   = gen_random_list('ABCD', N_Questions)
#      1~5  
SCORE = gen_random_list(range(1,6), N_Questions)

QUIZE = zip(ANS, SCORE)
students = [
    #       'ABCD*'   ,'*'      
    Student(_id, gen_random_list('ABCD*', N_Questions))
    for _id in range(1, N_Students+1)
]

print(QUIZE)
# [('A', 3), ('B', 1), ('D', 1), ...
print(students)
# [Student(id=1, ans=['C', 'B', 'A', ...

はじめに
まず、通常のプロセス向けプログラミングスタイルを見てみましょう.私たちは学生一人一人を遍歴し、学生一人一人の問題に対する答えを遍歴し、実際の答えと比較し、正しい答えの点数を累計する必要があります.
import data
def normal(students, quize):
    for student in students:
        sid = student.id
        score = 0
        for i in range(len(quize)):
            if quize[i][0] == student.ans[i]:
                score += quize[i][1]
        print(sid, '\t', score)

print('ID\tScore
==================') normal(data.students, data.quize) """ ID Score ================== 1 5 2 12 ... """

上のコードが非常に直感的で論理的だと思っている場合は、コンピュータの思考パターンに従って考えることに慣れていることを示します.ネストされた2つを作成することで  for  すべての問題の答えの判断と採点を循環して、これは完全にコンピュータのためにサービスする考え方で、Pythonの中の  for  サイクル比  C  言語はさらに進んで、通常は現在のループの回数を記録するために追加の状態変数を必要としないが、状態変数を使用しなければならない場合もある.上記の例の2番目のループで2つのリストの要素を比較しなければならない.関数式プログラミングの大きな特徴は、このような明示的なループをできるだけ捨てることではなく、現実の私のように問題解決そのものに注意を集中することである.答案用紙を添削するときは、2つの答えを並べて比較するだけです.
from data import students, QUIZE

student = students[0]

#                
#             
filtered = filter(lambda x: x[0] == x[1][0], zip(student.ans, QUIZE))

print(list(filtered))
# [('A', ('A', 3)), ('D', ('D', 1)), ...]

次に、すべての正しい問題の点数を加算します.
from functools import reduce

reduced = reduce(lambda x, y: x + y[1][1], filtered, 0)
print(reduced)

以上は1人の学生の結果処理であり、次にすべての学生に対して同じ処理を行うだけでよい.
def cal(student):
    filtered = filter(lambda x: x[0] == x[1][0], zip(student.ans, QUIZE))
    reduced = reduce(lambda x, y: x + y[1][1], filtered, 0)
    print(student.id, '\t', reduced)

print('ID\tScore
==================') # Python 3 map # list cal list(map(cal, students)) """ ID Score ================== 1 5 2 12 ... """

上記の例では  zip/filter/reduce/map  などの関数がデータ処理の方法をパッケージ化してデータに適用し、基本的な関数式のプログラミング操作を実現しました.しかし、関数式をもっと深く理解すれば、上の  cal  メソッドにグローバル変数が使用されています  QUIZEです.これにより、同じ入力条件で関数が異なる出力を生成する可能性があります.これはFPのタブーです.そのため、改善が必要です.
def cal(quize):
    def inner(student):
        filtered = filter(lambda x: x[0] == x[1][0], zip(student.ans, quize))
        reduced = reduce(lambda x, y: x + y[1][1], filtered, 0)
        print(student.id, '\t', reduced)
    return inner
map(cal(QUIZE), students)

このようにクローズドパッケージ(Closure)の方法で、純粋なFPモードを維持することができます!
火事(fn.py)
上のFPの書き方を見たかもしれませんが、あなたはやはりうるさいと思って、あなたが想像していた結果に達していません.この時、文法の砂糖利器を提出する必要があります.fn.py!fn.py よく使われるFP関数や文法糖を封入し、コードを大幅に簡素化します!
pip install fn

まず、さっきの閉パッケージから、この問題をよりFPで解決することができます.コリー化と呼ばれています.簡単に言えば、複数のパラメータを受け入れることができる関数は、毎回1つのパラメータしか受け入れられません.
from fn.func import curried
@curried
def sum5(a, b, c, d, e):
    return a + b + c + d + e

sum3 = sum5(1,2)
sum4 = sum3(3,4)
print(sum4(5))
# 15

上の  cal  メソッド:
from fn.func import curried

@curried
def cal(quize, student):
    filtered = filter(lambda x: x[0] == x[1][0], zip(student.ans, quize))
    reduced = reduce(lambda x, y: x + y[1][1], filtered, 0)
    print(student.id, '\t', reduced)

map(cal(QUIZE), students)

FPにおけるデータは、一般的に、一連の関数のパイプにおいてデータストリームが伝達されるものと見なされるので、上記のreduceおよびfilterは、実際には統合され得る.
reduce(lambda x, y: x + y[1][1], filter(lambda x: x[0] == x[1][0], zip(student.ans, quize)), 0)

さらに簡略化されたものの、これによりコードの可読性が大幅に低下し(これもFPが批判されやすい点である)、そのために  fn  より高度な関数操作ツールが用意されています.
from fn import F

cal = F() >> (filter, lambda x: x[0]==x[1][0]) >> (lambda r: reduce(_+_[1][1], r, 0))
#          
print(cal(zip(student.ans, QUIZE)))

#       
@curried
def output(quize, student):
    cal = F() >> (filter, lambda x: x[0]==x[1][0]) >> (lambda r: reduce(_+_[1][1], r, 0))
    print(student.id, '\t', cal(zip(student.ans, quize)))
map(output(QUIZE), students)

入魔(Hy)
上のコードがPython言語に見えないほど魔性があると思ったら、このような文法設定を受けたらいいと思います.LispやHaskellプログラマーに喜んで見せると、非情に軽蔑されるに違いありません.Python関数式プログラミングの奥義を掘り起こし続ける決意を固めました.おめでとうございます、組織はあなたの参加を歓迎します:Hail Hydra!
ああ、違います.漏れました.Hi Hyです.
Hy PythonベースのLisp方言で、Pythonコードと完璧に埋め込むことができます(PyPyを好む場合は、同じようにPixieもあります)、それ以外にも独立した言語として見ることができます.独自の解釈器があり、独立したスクリプト言語として使用することができます.
pip install git+https://github.com/hylang/hy.git

まず基本的な使い方を見てみましょう.Pythonと同じように、インストールが終わったら  hy  コマンドはREPL環境に入ります.
=> (print "Hy!")
Hy!
=> (defn salutationsnm [name] (print (+ "Hy " name "!")))
=> (salutationsnm "YourName")
Hy YourName!

または、コマンドラインスクリプトとして実行します.
#! /usr/bin/env hy
(print "I was going to code in Python syntax, but then I got Hy.")

名前を付けて保存  awesome.hy :
chmod +x awesome.hy
./awesome.hy

次に、上記の問題を例にとります.まず、Pythonコードから直接インポートできます.
(import data)

;;    Debug      
;;               
(defmacro printlst [it]
    `(print (list ~it)))

(setv students data.students)
(setv quize data.QUIZE)

(defn cal [quize]
  (fn [student]
    (print student.id
      (reduce
        (fn [x y] (+ x (last (last y))))
        (filter
          (fn [x] (= (first x) (first (last x))))
          (zip student.ans quize))
        0
      )
    )
  )
)

(printl (map (cal quize) students))

不安がある場合は、最初に定義したメソッドを直接呼び出して結果を比較することもできます.
;;        normal       fun.py    
(import fun)
(.normal fun students quize)

まとめ
簡単なデータ処理の問題を例にとると,Python関数式プログラミングを経て「火をつけて魔に入る」ことを試みた.の全過程です.まだ満足できないと思っているかもしれませんが、もっと純粋なFP体験をしたいなら、Haskellが一番いい選択になります.FPはデータストリームを異なる関数間で伝達し、不要な中間変数を省き、関数の純粋性を保証するなど、データ処理の過程で非常に役立ちます(Pythonはこの分野のライバルであるR言語自体が文法設計においてLisp言語の影響を受けることが多く,文法も変に見えるが,データ処理や統計分析に適している理由の一つでもある).
リファレンス
  • Tips»0 x 02-関数式プログラミング
  • Python HOWTOs » Functional Programming HOWTO
  • Hy's Doc
  • fn.py