スキームの深化







あなたが持っている唯一のツールがハンマーである場合、多くの異なるオブジェクトが釘のように見えます。

マーク・トウェイン





パート1スキームの概要

パート2スキームの深化

パート3 IronSchemeの練習



近づく



今こそ、Scheme言語の基本的な構成要素を学ぶ時です。 新しい言語の使用方法を学ぶ最良の方法は、書き始めることです。 言語の最も基本的な要素を分析することから、徐々にダイブを開始します。



IronSchemeインタープリターをREPLモードで起動し、以下のコマンドを入力することをお勧めします。



単一行コメントはセミコロンで始まり、行末まで有効です。

;     
      
      





Schemeプログラムは、括弧で囲まれ、スペースで区切られたリストで構成されます(s-expression)。 関数呼び出しは(fxyz ...)と記述されます。ここで、fは関数の名前で、x、y、z、...はオペランドです。

 (+ 2 2) ; => 4
      
      





ここでは、2つの数値を追加する操作を実行しました。



式はネストできます:

 (+ 2 (+ 1 1)) ; => 4
      
      





したがって、式はアトムまたは他の式で構成できます。 上記の例では、数字の「1」と「2」は原子であり、「(+ 2(+ 1 1))」と「(+ 1 1)」は式です。



プリミティブ



番号:

 9999999999999999999999 ; integers #b111 ; binary => 7 #o111 ; octal => 73 #x111 ; hexadecimal => 273 3.14 ; reals 6.02e+23 1/2 ; rationals 1+2i ; complex numbers
      
      





たとえば同じプリミティブから値のリストを作成するには、リスト関数を使用する必要があります。引数はリストに収集されます。 別の方法で計算を抑制できます。 抑制は、引用関数または「 "」リストの前に単一引用符を付けた対応する糖によって実現されます。 リストは計算を抑制しないことに注意してください。

 (list 1 2 3 4 5 (+ 1 2 3)) ; => (1 2 3 4 5 6) (quote (1 2 3 4 5 (+ 1 2 3))) ; => (1 2 3 4 5 (+ 1 2 3)) '(1 2 3 4 5 (+ 1 2 3)) ; => (1 2 3 4 5 (+ 1 2 3))
      
      





いくつかの算術演算



 (+ 1 1) ; => 2 (- 8 1) ; => 7 (* 10 2) ; => 20 (expt 2 3) ; => 8 (quotient 5 2) ; => 2 (remainder 5 2) ; => 1 (/ 35 5) ; => 7 (/ 1 3) ; => 1/3 (exact->inexact 1/3) ; => 0.3333333333333333 (+ 1+2i 2-3i) ; => 3-1i
      
      







ブール代数



真実を示すプリミティブな「#t」があり、falseは「#f」として指定されます。さらに、「#f」以外のすべての値はtrueとして扱われます。

 (not #t) ; => #f (and 0 #f) ; => #f (or #f 0) ; => 0
      
      







文字、文字列



RnRs規格によれば、文字は2つの方法でコードで表すことができます。それ自体を指定する文字または文字コードです。

 #\A ; => #\A #\x03BB ; => #\λ
      
      





文字列は、固定長の文字の配列であり、二重引用符で囲まれています。

 "Hello, world!"
      
      





文字列内の引用符はバックスラッシュでエスケープできます:

 "Benjamin \"Bugsy\" Siegel"
      
      





行を標準出力に印刷するには、引数として行をとるdisplay関数を使用できます。

 (display "Some string")
      
      





行は組み合わせることができます:

 (string-append "Hello " "world!") ; => "Hello world!"
      
      





次のようなインデックスを使用して、文字列の文字にアクセスできます。

 (string-ref "Apple" 0) ; => #\A
      
      





文字列をフォーマットするには、フォーマット機能を使用すると便利です。

 (format "~a can be ~a" "strings" "formatted") ; => "strings can be formatted"
      
      





フォーマット結果が失われないようにするには、変数に文字列を割り当てることができます。

 (define str (format "~a can be ~a" "strings" "formatted"))
      
      







変数



「define」関数を使用して変数を宣言できます。最初の引数は関数の名前、2番目のオプションの引数は変数を初期化する値です。 変数名には、次の文字を除く任意の文字を含めることができます:()[] {} ''、 '';#/ \

例:

 (define some-var 5) some-var ; => 5
      
      





Lispの変数は強く型付けされておらず、原子または関数を指すことができます。

操作「set!」は、値を変数に格納します。実際には、他の言語からの代入演算子「=」に類似しています。 最初に変数を「定義」操作として宣言することを忘れないでください。

 (define my-name "unknown") my-name ; => "unknown" (set! my-name "NalaGinrut") my-name ; => " NalaGinrut "
      
      





以前に宣言されていない変数にアクセスしようとすると、例外がスローされます。

コンストラクト(let ...)を使用してローカル変数のグループをすぐに宣言すると便利です。

 (let ( (a "My") (b "name") (c "is") (d "Bob") ) (set! d "Sara") (format "~a ~a ~a ~a" abcd) ) ; => My name is Sara
      
      







機能



関数を作成するには、(lambda(abc)body)コンストラクトを使用します。a、b、cは引数、bodyはコマンドのシーケンスです。

 (lambda () "Hello World") (lambda (x) (+ xx))
      
      





上記で作成された関数には名前がないため、アクセスする方法はありません。 作成された関数にアクセスするには、変数に割り当てることができます

 (define hello-world (lambda () "Hello World")) (hello-world) ; => "Hello World"
      
      





または:

 (define hello-world) (set! Hello-world (lambda () "Hello World")) (hello-world) ; => "Hello World"
      
      





通常、より便利な構造が使用されます(define(function-name arg1 arg2)body)

 (define (mul ab) (* ab)) (mul 2 3); => 6
      
      





関数は常に最後の値を返します。

 (define (last-string) “one” “two” “three”) (last-string) ; => “three”
      
      







フロー制御



Schemeでの分岐には、最もよく知られているが、常に最も便利なif-then-else型の構造のさまざまな構造があります。

 (if #t ;  "this is true" ;   "this is false") ;   ; => "this is true"
      
      





ブランチ内のブランチで複数のコマンドを実行する必要がある場合は、それらを「begin」ブロックで囲む必要があります

 (if (< 1 3) (begin (display “one line”) (newline) (display “two line”) (- 1 3) ) )
      
      





いくつかの条件を確認する必要がある場合、「cond」設計が便利です

 (cond ((> 2 2) "wrong!") ((< 2 2) "wrong again!") ((= 2 2) "ok") (else "wrong also") )
      
      





真実の場合にのみコードを実行する必要がある場合、「いつ」が完璧です

 (when (< 1 3) “true”)
      
      





循環を整理するには、再帰という2つの方法があります

 (define (lp i) (when (< i 10) (display (format "i=~a\n" i)) (lp (+ 1 i)) ) ) (lp 5) ; => i=5, i=6, ...
      
      





または、名前付きの「let」を使用して

 (let loop ((i 0)) ; definition (when (< i 10) ; condition (display (format "i=~a\n" i)) ; body (loop (+ 1 i)) ; next iteration ) ) ; => i=0, i=1, ...
      
      







マクロ



スキームマクロは、言語の構文を拡張して新しい構成を作成できる、かなり強力なツールです。 ただし、あまりにも夢中になって、本当に必要な場合にのみマクロを適用しないでください。 マクロ定義はdefine-syntaxコマンドで始まります

 (define-syntax macro (syntax-rules (<keywords>) ((<pattern>) <template>) ... ((<pattern>) <template>) ) )
      
      





<keywords>-テンプレートの説明で使用できるキーワード。 たとえば、「(forch(items in item)...)」というデザインのマクロを作成できます。この場合、キーワードは「in」であり、存在する必要があります。



<pattern>-マクロの入力内容を記述するテンプレート。



<template>-変換先を記述するテンプレートマクロでは、省略記号「...」は、本文に1つ以上のフォームを含めることができることを意味します。



マクロを使用してwhileループとforループを作成することを検討してください。

 (define-syntax while (syntax-rules () ((while condition body ...) (let loop () (when condition body ... (loop) ) ) ) ) )
      
      





作成されたマクロを確認する

 (define iter 0) (while (< iter 10) (set! iter (+ iter 1)) (displayln iter )) ; => 1 2 3 …
      
      





forループのマクロを定義します。

 (define-syntax for (syntax-rules () ((for (iterator from to) body ...) (let loop((iterator from)) (when (< iterator to) body ... (loop (+ 1 iterator)) ) ) ) ) )
      
      





チェック:

 (for (i 0 15) (displayln i)) ; => 1 2 3 ...
      
      







例外



人生では、実行中に例外が発生する可能性のある不安定なコードを使用する必要があることは珍しくありません。 Lisp、特にSchemeには、高度な例外処理システムがあります以下は、プログラムがクラッシュすることを恐れずに例外を処理する方法の簡単な例です。

 (guard (cond ;  c    (display (condition-message cond)) ;   ) (/ 1 0) ;    )
      
      





標準で記述されているScheme言語の基本のいくつかを学びました。 もちろん、この記事では言語のすべての機能について説明しているわけではありません;さもないと、記事が大きすぎて、実際には標準の翻訳になります。 しかし、実際に役立つSchemeアプリケーションを開発するには、私たちが学んだことで十分です。



All Articles