1.11k likes | 1.29k Views
8. 高階関数を用いた抽象化. 例題. 例題1.接線の傾き. 接線の傾きを求める関数 d/dx を定義し,実行する 数値 x , h と関数 f から, x における f の傾き ( つまり f '(x) ) の近似値を求める. 傾きは. f(x). 接線. x. 0. 入力と出力. f2 3 0.0001. f2'(3) の近似値. d/dx. 出力. 入力. 出力は数値. 入力は2つの数値と 関数. d/dx は,関数を入力とするような関数 (つまり高階関数). Contract ( 規約 ).
E N D
例題1.接線の傾き • 接線の傾きを求める関数 d/dxを定義し,実行する • 数値x, hと関数 fから,xにおける fの傾き (つまり f'(x) ) の近似値を求める 傾きは f(x) 接線 x 0
入力と出力 f2 3 0.0001 f2'(3) の近似値 d/dx 出力 入力 出力は数値 入力は2つの数値と 関数 d/dxは,関数を入力とするような関数 (つまり高階関数)
Contract (規約) d/dx 関数の Contract(規約) d/dx: (number->number) number number -> number 関数 入力:数値 出力:数値 数値 数値 数値 出力 入力
d/dx 関数 ;; d/dx: (number->number) number number -> number ;; inclination of the tangent ;; Example: (d/dx f 3 0.0001) ;; = ((- (f 3.0001) (f 2.9999)) 0.0002) (define (d/dxf x h) (/ (- (f (+ x h)) (f (- x h))) (* 2 h))) 関数が, 「d/dx」の入力になっている
次に関数 f2を定義している (define (f2x) (- (* x x) 2))
これは, (d/dxf2 3 0.0001) と書いて,xの値を 3 に, hの値を 0.0001 に, fを f2に設定して実行 実行結果である「6」が 表示される
(d/dx f2 3 0.0001) から 6 が得られる過程の概略 最初の式 (d/dx f2 3 0.0001) = (/ (- (f2 (+ 3 0.0001)) (f2 (- 3 0.0001))) (* 2 0.0001)) = ... = (/ (- (- (* 3.0001 3.0001) 2) (f2 (- 3 0.0001))) (* 2 0.0001)) = ... = 6 コンピュータ内部での計算 実行結果 但し,f2は (define (f2x) (- (* x x) 2))
(d/dx f2 3 0.0001) から 6 が得られる過程の概略 (d/dx f2 3 0.0001) = (/ (- (f2 (+ 3 0.0001)) (f2 (- 3 0.0001))) (* 2 0.0001)) = ... = (/ (- (- (* 3.0001 3.0001) 2) (f2 (- 3 0.0001))) (* 2 0.0001)) = ... = 6 これは, (define (d/dxf x h) (/ (- (f (+ x h)) (f (- x h))) (* 2 h))) の xを 3 で,hを 0.0001 で, fを f2で置き換えたもの
(d/dx f2 3 0.0001) から 6 が得られる過程の概略 (d/dx f2 3 0.0001) = (/ (- (f2 (+ 3 0.0001)) (f2 (- 3 0.0001))) (* 2 0.0001)) = ... = (/ (- (- (* 3.0001 3.0001) 2) (f2 (- 3 0.0001))) (* 2 0.0001)) = ... = 6 これは, (define (f2x) (- (* x x) 2)) の xを (+ 3 0.0001)=3.0001で置き換えたもの
例題2.べき級数 • べき級数を求める高階関数 power_seriesを定義する • 自然数 n,数x,関数 g から (* (g 0)(expt x 0)) + (* (g 1)(expt x 1)) + …+(* (g n)(expt x n)) を求める • n = 0 の時 • n > 0 の時
級数とべき級数 • 級数 (series関数で実現可) • べき級数 • べき級数の各項を f(k) と考えると級数と同じ手順で計算できることが分かる • この f(k) には,関数 g と値 x を与えないと計算できない
Contract (規約) power_series 関数の Contract(規約) ;;power_series: number N (N->number) -> number 数値 自然数 関数 入力:自然数 出力:数値 数値 出力 入力
入力と出力 (g1 0), (* (g1 1) (expt 2 1)), (* (g1 2) (expt 2 2)), (* (g1 3) (expt 2 3)) の値の合計 2 3 g1 power_series 出力 入力 出力は数値 入力は2つの数値と 関数 power_seriesは,関数を入力とするような関数 = 高階関数
べき級数 power_series 関数 ;; series: N (N -> number) -> number ;; sum up the first n numbers in the sequence f ;; (series 3 f2) = (+ (f2 3) (+ (f2 2) (f2 1))) (define (seriesn f) (cond [(= n 0) (f 0)] [else (+ (f n) (series (- n 1) f))])) ;; power_series: number N (N -> number) -> number (define (power_seriesx n g) (local ((define (power_termi) (* (gi) (expt x i)))) (seriesn power_term) ) )
定義の局所化 (local式) ;; series: N (N -> number) -> number ;; sum up the first n numbers in the sequence f ;; (series 3 f2) = (+ (f2 3) (+ (f2 2) (f2 1))) (define (seriesn f) (cond [(= n 0) (f 0)] [else (+ (f n) (series (- n 1) f))])) ;; power_series: number N (N -> number) -> number (define (power_seriesx n g) (local ((define (power_termi) (* (gi) (expt x i)))) (seriesn power_term) ) ) series関数: 級数 Σf(k) を求める 関数 power_termは,関数 power_series内の 局所(local)関数として定義されている
べき級数 power_series 関数 ;; series: N (N -> number) -> number ;; sum up the first n numbers in the sequence f ;; (series 3 f2) = (+ (f2 3) (+ (f2 2) (f2 1))) (define (seriesn f) (cond [(= n 0) (f 0)] [else (+ (f n) (series (- n 1) f))])) ;; power_series: number N (N -> number) -> number (define (power_seriesx n g) (local ((define (power_termi) (* (gi) (expt x i)))) (seriesn power_term) ) )
関数 series,関数 power_series を定義している
関数 g1を定義している (define (g1n) n)
これは, (power_series 2 3 g1) と書いて,xの値を 2に,nの値を 3に,gの値を g1に設定して実行 実行結果である「34」が 表示される
べき級数 power_series の計算例(概略) 最初の式 power_series を使い簡単なべき級数 x + 2x2 +…+ nxn を計算 但し x=2, (define (g1n)n) (power_series 2 3 g1) = (local ((define (power_term i) (* (g1 i) (expt 2i)))) (series 3 power_term)) = (local ((define (power_term i) (* (g1 i) (expt 2i)))) (+ (power_term 3) (series 2 power_term)))) = ... = (local ((define (power_term i) (* (g1 i) (expt 2i)))) (+ (power_term 3) (+ (power_term 2) (+ (power_term 1) (power_term 0))))) = ... = (+ (* (g1 3)(expt 2 3)) (+ (* (g1 2)(expt 2 2)) (+ (* (g1 1)(expt 2 1)) (* (g1 0)(expt 2 0))))) = ... = (+ 24 (+ 8 (+ 2 0))) = ... = 34 実行結果
「(power_series 2 3 g1)」 は 関数の中身で置き換わる
「power_term」は局所関数「power_term_0」で 置き換わる.(_0は局所関数を示す番号)
「(series 3 power_term_0)」 は 関数の中身で置き換わる
「(= 3 0)」 は 「false」で置き換わる
「(cond (else ...))」 は 「...」の部分で置き換わる
「(power_term_0 3)」 は 関数の中身で置き換わる
「(g1 3)」 は 「3」で置き換わる
「(expt 2 3)」 は 「8」で置き換わる
「(* 3 8)」 は 「24」で置き換わる
「(- 3 1)」 は 「2」で置き換わる
「(series 2 power_term_0)」 は 関数の中身で置き換わる
「(= 2 0)」 は 「false」で置き換わる
「(false (power_term_0 0))」 は 消える 以後省略
実習の進め方 • 資料を見ながら,「実習」を行ってみる • その後,各自「課題」に挑戦する • 各自で自習 + 巡回指導 • 遠慮なく質問してください • 自分のペースで先に進んで構いません
DrScheme の使用 • DrScheme の起動 プログラム → PLT Scheme → DrScheme • 今日の実習では「Intermediate Student」 に設定 Language → Choose Language → Intermediate Student → OK ボタン
DrScheme の使用 / ステップ実行 • プログラム実行の振る舞いを観察するツール • 「定義用ウインドウ」のみを使用 (通常の実行と異なる) • 「Intermediate Student」に設定する必要あり Language → Choose Language → Intermediate Student → Run ボタン
実習1.階乗 • 階乗を計算する関数 !を定義し,実行する • 次の方針でプログラムを作成する n > 0 のとき,n! = n × (n - 1)! 例) 6! = 6×5!
階乗 • n = 0 のとき • n > 0 のとき
「実習1.階乗」の手順 • 次を「定義用ウインドウ」で,実行しなさい • 入力した後に,Run ボタンを押す ;;! : number -> number ;;to compute n*(n-1)*...*2*1 (define (!n) (cond [(= n 0) 1] [else (* n (! (- n 1)))])) • その後,次を「実行用ウインドウ」で実行しなさい (! 3) (! 4)
入力と出力 24 4 ! 出力 入力 入力は数値 出力は数値
! 関数 ;; ! : number -> number ;; to compute n*(n-1)*...*2*1 ;; Example: (! 4) = 24 (define (!n) (cond [(= n 0) 1] [else (* n (! (- n 1)))])) 0! = 1 である n! は (n-1)! を計算して,n を掛ける
階乗 • n = 0 ならば: → 終了条件 1 → 自明な解 • そうで無ければ: • 「(n – 1) の階乗」に n をかける ⇒ 結局,1 から n までのかけ算を繰り返す
階乗 ;; ! : number -> number ;; to compute n*(n-1)*...*2*1 ;; Example: (! 4) = 24 (define (!n) (cond [(= n 0) 1] [else (* n (! (- n 1)))])) 自明な解 終了条件
終了条件 No (= n 0) Yes (* n (! (- n 1))) 1 が自明の解