見出し画像

関数型プログラミング事始め (21) 関数はデータ - Lisp超入門10

関数型プログラミングがはじめての方へ贈る入門の書
前節:継承の事 次節:マクロは高機能
参考書:
・五味 弘「はじめてのLisp関数型プログラミング」技術評論社(2016)
・大山口 通夫、五味 弘「プログラミング言語論」コロナ社(2008)
・五味 弘「関数型プログラミングと数学(ITと数学)」技術評論社(2021)

Lispでは関数はデータです。整数や配列などの普通のデータと同じ感覚で使えます。関数は変数に格納でき、関数の引数や配列の要素としても使え、また関数の値としても使えます。これをファーストクラスオブジェクト(第一級データ型)と呼んでいます。このように関数はデータと同じなので、動的に関数を定義でき、動的に関数呼び出しができます。これにより動的プログラミングができます。またLispでは関数引数やその値に関数を適用する高階関数のライブラリ関数も用意されています。

(13) 関数

Lispでは関数は整数や配列などの普通のデータと同じように扱えます。動的にそのデータを定義(生成)し、それを変数に格納したり、関数引数にしたり、配列の要素としたり、関数の値にしたりできるファーストクラスオブジェクトです。

ここではこの関数について紹介していきます。まずはislispを起動してください。なおISLisp処理系はLisp処理系の導入で紹介していますので参照してください。

> ISLisp Version 0.80 (1999/02/25)
>
ISLisp>

(a) 関数定義

Lispでは関数オブジェクトはfunctionで既存の関数オブジェクトを扱えます。一方、Lispでは関数を動的に定義(生成)できます。defun(define function)で名前付きの関数が定義でき、さらにlambdaで匿名関数(無名関数)を定義できます。

ISLisp>(function +)
#<SFUNCTION 00001032: +>
ISLisp>#'+
#<SFUNCTION 00001032: +>
ISLisp>(defun add (x y) (+ x y))
ADD
ISLisp>#'add
#<UFUNCTION 0026FE36: ADD>
ISLisp>(lambda (x y) (+ x y))
#<UFUNCTION 0026FC36: #<LAMBDA>>

(function +)で関数+のオブジェクトを返します。なおSFUNCTIONはシステム定義関数のことでOK! ISLispではこのように表記しています。
#'+は(function +)の構文糖衣(syntax sugar)のことでfunctionの略記になります。
defunで関数addを定義しています。#'addで定義した関数addのオブジェクトを返します。なおUFUNCTIONはユーザ定義関数のことでOK! ISLispではこのように表記しています。また関数定義はLispは前置形式でも紹介しています。
(lambda (x y) (+ x y))で匿名関数を定義(生成)しています。

(b) 関数呼び出し

次にデータとしての関数を呼び出すにはfuncallapplyが使えます。ここではfuncallを紹介します。

ISLisp>(funcall #'+ 1 2)
3
ISLisp>(funcall #'add 1 2)
3
ISLisp>(funcall (lambda (x y) (+ x y)) 1 2)
3
ISLisp>(defglobal add2 (lambda (x y) (+ x y)))
ADD2
IISLisp>add2
#<UFUNCTION 0026F836: #<LAMBDA>>
ISLisp>(funcall add2 1 2)
3
ISLisp>((lambda (x y) (+ x y)) 1 2)
3

(funcall #'+ 1 2)はfuncallの第1引数の#'+の関数に、引数として1,2を与えて、関数呼び出しをします。そして結果の3の返します。
(funcall #'add 1 2)も上記と同様に関数addを呼び出します。
また(funcall (lambda (x y) (+ x y)) 1 2)も同様に匿名関数(lambda (x y) (+ x y))を呼び出します。
(defglobal add2 (lambda (x y) (+ x y)))では、関数を変数add2に格納しています。(funcall add2 1 2)で変数add2に格納された関数を呼び出すことができます。
なおラムダ式は直接関数呼び出しができ、((lambda (x y) (+ x y)) 1 2)のようにfuncallを使う必要もありません。

(c) 関数はファーストクラスオブジェクト

Lispでは関数はファーストクラスオブジェクトです。普通のデータと同じように、どこでも適用可能です。これから動的なプログラミングができます。

動的プログラミングとは、関数、つまりプログラムそのものをプログラムの実行中に定義(生成)でき、プログラムが動的に変更されます。

普通のコンピュータアーキテクチャであるストアードプログラム方式(プログラム格納方式)であれば、プログラムとデータは格納領域に格納される形式ですから、プログラムの動的変更も不可能ではありません。実際、アセンブラレベルでは当然のように可能です。

しかし多くのコンパイラ方式のプログラミング言語では動的に変更できません。Lispはインタプリタ言語ですが、コンパイラも持っています。さらに多くのLisp処理系ではコンパイラも関数なので、動的にコンパイルされ、結果的にコンパイルも含めて動的プログラミングが可能です。

この関数がファーストクラスオブジェクトであることが関数型プログラミングの要諦のひとつです。これを使って高階関数や遅延評価などの便利な機能が使えます。それになにより自由にプログラミングできます。

これについては、ラムダの苦難高階関数の苦難遅延評価の苦難の記事を参考にしてください。なお苦難となっていますが、苦あれば楽がありますので、安心してください。

(次回予告)Lisp超入門11

次回もOK! ISLisp処理系を使って、Lispの超入門の第11回を紹介する予定です。お楽しみに。

参考:プログラミング言語はどれがお得?(前編)|五味弘 (note.com)
参考:プログラミング言語はどれがお得?(後編)|五味弘 (note.com)


よろしければサポートをお願いします!