4. 関数を定義しよう

簡単な関数の定義とロード

グローバル変数を宣言し, それに値を結びつけるには define という命令を使う.

define は通常の変数の宣言の他に, 関数を定義するためにも使われる.

注意:

Scheme には関数と通常の変数の区別がない

hello.scm

1
2
3
4
5
6
; Hello Scheme World as a variable
(define vhello "Hello Scheme World")

; Hello Scheme World as a function
(define fhello (lambda ()
		 "Hello Scheme World"))

hello.scm の実行結果は:

guile> (load "hello.scm")
guile> vhello
"Hello Scheme World"
guile> fhello
#<procedure fhello ()>
guile> (fhello)
"Hello Scheme World"

注意:

fhello を手続きとして呼び出すためには (fhello) というように括弧でくくる.

決まった数の引数をとる場合

引数を取る関数を定義するときは lambda の後ろに引数のリストを置けば, それが引数となる.

farg.scm

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
; hello with name
(define hello
  (lambda (name)
    (string-append "Hello " name "!")))

; sum of three numbers
(define sum3
  (lambda (a b c)
    (+ a b c)))

; a + b
(define add
  (lambda (a b)
    (+ a b)))

; a - b
(define min
  (lambda (a b)
    (- a b)))

; a * b
(define mul
  (lambda (a b)
    (* a b)))

; a / b
(define div
  (lambda (a b)
    (/ a b)))

farg.scm の実行結果は:

guile> (load "farg.scm")
guile> (hello "feifei")
"Hello feifei!"
guile> (sum3 1 2 3)
6
guile> (add 1 2)
3
guile> (mul 1 2)
2
guile> (div 1 2)
1/2

任意個の引数を取る方法

任意個の引数をとる関数を定義することができる. ゼロ個以上の固定された引数に加えて, 個数が不定の引数 (レストパラメータ) をとることができる.

個数が不定の引数は, リストとして関数本体に渡る. 今までの説明範囲では使い道はないのであるが, 繰り返しや, 高階関数とともに利用するとエレガントなコードを書くことができる.

以下の例は, 関数に渡った引数をリストにして返す関数である. 3つの通常の引数とレストパラメータをとる. 通常の引数の後に, ドットで区切って, レストパラメータを書く.

args.scm

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
(define 3args+
  (lambda (a b c . d)
    (list a b c d)))

(define args0.1
  (lambda ( . d)
    (list d)))

(define args1.1
  (lambda (c . d)
    (list c d)))

(define args2.1
  (lambda (b c . d)
    (list b c d)))

args.scm の実行結果は:

guile> (load "args.scm")
guile> (3args+ 1 2 3)
(1 2 3 ())
guile> (3args+ 1 2 3 4)
(1 2 3 (4))
guile> (3args+ 1 2 3 4 5)
(1 2 3 (4 5))
guile> (3args+ 1 2 3 4 5 6)
(1 2 3 (4 5 6))
guile> (3args+ 1 2 3 4 5 6 7)
(1 2 3 (4 5 6 7))


guile> (args0.1)
(())
guile> (args0.1 1)
((1))
guile> (args0.1 1 2)
((1 2))
guile> (args0.1 1 2 3)
((1 2 3))
guile> (args1.1 1)
(1 ())
guile> (args1.1 1 2)
(1 (2))
guile> (args1.1 1 2 3)
(1 (2 3))
guile> (args2.1 1 2)
(1 2 ())
guile> (args2.1 1 2 3)
(1 2 (3))
guile> (args2.1 1 2 3 4)
(1 2 (3 4))

関数定義の省略形

lambda を使って関数を定義するのが正統な関数の定義法であるが, 下記の省略形が使える.

unregular.scm

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
;; Hello with Name
(define (hello name)
  (string-append "Hello " name "!"))

;; sum of four numbers
(define (sum4 a b c d)
  (+ a b c d))

;; returning a list of three and additional arguments
(define (args3.1 a b c . d)
  (list a b c d))

unregular.scm の実行結果は:

guile> (load "unregular.scm")
guile> (hello "feifei")
"Hello feifei!"
guile> (sum4 1 2 3 4)
10
guile> (args3.1 1 2 3)
(1 2 3 ())
guile> (args3.1 1 2 3 4)
(1 2 3 (4))
guile> (args3.1 1 2 3 4 5)
(1 2 3 (4 5))
guile> (args3.1 1 2 3 4 5 6)
(1 2 3 (4 5 6))

練習問題 1

次の書いてください:

1. 引数に 1 を加えて返す関数 (+1)
2. 引数から 1 を引いて返す関数 (-1)

addmin.scm

1
2
3
4
5
6
7
;; plus 1
(define (add1 x)
  (+ x 1))

;; minus 1
(define (min1 x)
  (- x 1))

addmin.scm の実行結果は:

guile> (load "addmin.scm")
guile> (add1 5)
6
guile> (min1 5)
4
guile> (add1 (add1 5))
7
guile> (min1 (min1 5))
3
guile> (min1 (add1 5))
5

練習問題 2