第4回 再帰とデータ型の話をしよう
山下 伸夫
株式会社タイムインターメディア
2009/2/16
関数型言語に分類されるHaskell。C言語などの手続き型言語とまったく異なるプログラミングの世界に踏み出してみよう(編集部)
まず、再帰の話をしよう。
といっても難しい話はできないのでしない。ここでは、再帰は問題のありさまを表現するための方法の1つだ思うことにする。
では具体的な問題を考えてみよう。
階乗を計算する関数
階乗を計算する関数を考えてみよう。
ある正整数をnとすると、n!(nの階乗)とはn×(n-1)×…×2×1のことである |
階乗を計算するときの計算方法はいろいろありそうだが、
n!=n×(n-1)×…×2×1=n×(n-1)!
つまり、
n!は(n-1)!にnを掛けたものである
となる。ここから、計算方法は、
n!を計算するには、一歩手前の(n-1)!が分かったとして、それにnを掛ける
と考えることができるのである。nからn!への関数をfactとすると、この計算はHaskellでは、
fact n = n * fact (n - 1) |
と表現できる。
ここでfactを定義するのにfactを使っている。このような定義の仕方を再帰的定義という。少し奇妙な感じがするかもしれない。fact nを定義するのにfact (n-1)、つまりfact nとは別のものを使っていると思えば、この感覚は治まるかもしれない。
さて、実際に計算してみよう。この定義をfact.hsというファイルに保存して、ghciにロードする。
$ ghci fact.hs |
fact.hs |
例えば、9!を計算をしてみよう。プロンプトにfact 9を入力すると、
*Main> fact 9 |
と表示される。
何が起こったのだろう。もう一度、
fact n = n * fact (n - 1) |
という定義を見てみると、fact 9の値を計算するには、9の値とfact 8の値が必要で、fact 8の値を計算するには、8の値とfact 7の値が必要、fact 7の値を計算するに、7の値とfact 6の値が必要、fact 6の値を計算するには……と、どこまでいってもきりがないのである。
階乗の値は最初の定義では1以上のnについて意味があるので、n≦1であるようなnの一歩手前というのは存在しない。また、1!は1であるとしておくのが妥当なので、n=1のときは特別扱いして、factの定義を以下のようにしよう。
fact :: Integer -> Integer -> Integer |
今度はちゃんと動くはずだ。
階段の昇り方
もう1つ問題を考えよう。
階段を1度に1段または1段飛しで(つまり1度に2段)昇るものとする。階段の段数を与えられたとき、その昇り方が何通りあるかを表現する関数を定義せよ |
これも前節と同じように考えると簡単に定義できる。
何度か昇って、いまn段目にいるとすると、直前には(n-1)段目にいたか、(n-2)段目にいたかのどちらかである。ということは、n段目に到達する昇り方の数は、(n-1)段目への昇り方の数と(n-2)段目への昇り方の数の和になるはずだ。
n段目への昇り方の数をstepWays nと表現すると、
stepWays n = stepWays (n-1) + stepWays (n-2) |
となる。これだけでは先ほどと同じようなエラーになってしまうので、もう少し考えよう。
- いま1段目にいるとすると、直前にいたのは0段目
- いま0段目にいるとすると、直前というのはなく、そこにいるわけだから0段目への昇り方は1通りと考えるのが妥当
というわけで、stepWaysは次のように定義できる。
stepWays :: Integer -> Integer |
では、5段目まで昇ってみよう。
*Main> stepWays 5 |
図で説明したように5段目までなら8通りの昇り方があることが計算できている。
このセクションおよび前セクションでの考え方は、典型的な再帰の考え方である。課題を解く関数の再帰的定義が、課題を素直に表現したものになっていることに注目してほしい。
1/3 |
Index | |
再帰とデータ型の話をしよう | |
Page1 階乗を計算する関数 階段の昇り方 |
|
Page2 計算の効率 データ型の話をしよう 論理値型 数値型 |
|
Page3 文字と文字列 リスト 文字列は文字のリスト |
のんびりHaskell |
Coding Edgeお勧め記事 |
いまさらアルゴリズムを学ぶ意味 コーディングに役立つ! アルゴリズムの基本(1) コンピュータに「3の倍数と3の付く数字」を判断させるにはどうしたらいいか。発想力を鍛えよう |
|
Zope 3の魅力に迫る Zope 3とは何ぞや?(1) Pythonで書かれたWebアプリケーションフレームワーク「Zope 3」。ほかのソフトウェアとは一体何が違っているのか? |
|
貧弱環境プログラミングのススメ 柴田 淳のコーディング天国 高性能なIT機器に囲まれた環境でコンピュータの動作原理に触れることは可能だろうか。貧弱なPC上にビットマップの直線をどうやって引く? |
|
Haskellプログラミングの楽しみ方 のんびりHaskell(1) 関数型言語に分類されるHaskell。C言語などの手続き型言語とまったく異なるプログラミングの世界に踏み出してみよう |
|
ちょっと変わったLisp入門 Gaucheでメタプログラミング(1) Lispの一種であるScheme。いくつかある処理系の中でも気軽にスクリプトを書けるGaucheでLispの世界を体験してみよう |
|
- プログラムの実行はどのようにして行われるのか、Linuxカーネルのコードから探る (2017/7/20)
C言語の「Hello World!」プログラムで使われる、「printf()」「main()」関数の中身を、デバッガによる解析と逆アセンブル、ソースコード読解などのさまざまな側面から探る連載。最終回は、Linuxカーネルの中では、プログラムの起動時にはどのような処理が行われているのかを探る - エンジニアならC言語プログラムの終わりに呼び出されるexit()の中身分かってますよね? (2017/7/13)
C言語の「Hello World!」プログラムで使われる、「printf()」「main()」関数の中身を、デバッガによる解析と逆アセンブル、ソースコード読解などのさまざまな側面から探る連載。今回は、プログラムの終わりに呼び出されるexit()の中身を探る - VBAにおけるFileDialog操作の基本&ドライブの空き容量、ファイルのサイズやタイムスタンプの取得方法 (2017/7/10)
指定したドライブの空き容量、ファイルのタイムスタンプや属性を取得する方法、FileDialog/エクスプローラー操作の基本を紹介します - さらば残業! 面倒くさいエクセル業務を楽にする「Excel VBA」とは (2017/7/6)
日頃発生する“面倒くさい業務”。簡単なプログラミングで効率化できる可能性がある。本稿では、業務で使うことが多い「Microsoft Excel」で使えるVBAを紹介する。※ショートカットキー、アクセスキーの解説あり
|
|