gauche のスクリプトで、web ページにアクセスしてみたく、
(use rfc.http) (http-get server request-uri :secure #t) 例えば (http-get "artscape.jp" "/exhibition/traveling/index.html" :secure #t)
としてみたところ、サイトによって、
*** ERROR: TLS handshake failed: -26112 Stack Trace: _______________________________________ 0 (connect-socket) ...
となってしまうところがありました。 (Gauche のコンパイルでは --with-tls=mbedtls-internal をつけ、(http-secure-connection-available?) => #t は確認してあります。)
この原因としてはどういうことが考えられて、対応方法はなにかありましょうか。
*** ERROR: TLS handshake failed: SSL - A field in a message was incorrect or inconsistent with other fields (-26112)プラットフォーム、とは以下でいいでしょうか。
$ ~/local/bin/gosh -V Gauche scheme shell, version 0.9.12 [utf-8,pthreads], x86_64-pc-linux-gnu ... (gauche.net.tls mbedtls) (Arch Linux でソフトウェアはディストリのパッケージでほぼ最新)状況報告だけですが、取り急ぎ。
$ ~/local_test/bin/gosh -V Gauche scheme shell, version 0.9.12 [utf-8,pthreads], x86_64-unknown-freebsd13.0 ... (build.configure "--with-tls=mbedtls-internal" "--enable-multibyte=utf-8") ... (gauche.net.tls mbedtls)
*** ERROR: mbedTLS: tls-ca-bundle-path isn't set. It is required to validate server certs.
tools/tls/Makefile.in
のMBEDTLS_VERSION
を2.27.0にしてconfigureからやってみてください。
C:/Gauche/Gauche/tools/tls/mbedtls-3.1.0/library/ssl_cli.c:2801: server used HashAlgorithm 2 that was not offered C:/Gauche/Gauche/tools/tls/mbedtls-3.1.0/library/ssl_cli.c:3108: bad server key exchange messageHashAlgorithm 2 というのが、SHA-1 のことになります。
gosh$ (use rfc.http) gosh$ (http-get "artscape.jp" "/exhibition/traveling/index.html" :secure #t) "200" (("date" "Thu, 15 Sep 2022 04:01:08 GMT") ("server" "Apache") ("x-frame-options" "SAMEORIGIN") ("accept-ranges" "bytes") ("transfer-encoding" "chunked") ("content-type" "text/html")) ...ただ、SHA-1 の使用は危険とのことで、よくなさそうですが。
Connection - obsolete connection settings The connection to this site is encrypted and authenticated using TLS 1.2, ECDHE_RSA with P-256, and AES_128_GCM. ・The server signature uses SHA-1, which is obsolete. Enable SHA-2 signature algorithm instead. (Note this is different from the signature in the certificate.)前回のパッチだと、mbed_connect_common() に追加した、前半の 3 行の
mbedtls_x509_crt_profile crt_profile_for_test = mbedtls_x509_crt_profile_default; crt_profile_for_test.allowed_mds |= MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA1 ); mbedtls_ssl_conf_cert_profile( &t->conf, &crt_profile_for_test );が (A) の許可に対応し、残りの 1 行の
mbedtls_ssl_conf_sig_hashes( &t->conf, ssl_sig_hashes_for_test );が (B) の許可に対応するようです。
「TLS/SSL のサポート」のコンパイルオプションの説明の部分の2箇所について、
`--with-tls=mbedtls` :: include only axTLS support
mbedtls と axTLS が整合してない気が。
`--with-tls=mbedtls--internal` :: During the build, MbedTLS source is downloaded
mbedtls--internal は - が一つ多い?
細かいところですが気づいてしまったのでご報告まで。
かなり基本的なことかもしれませんがよろしくお願いします。
(define number_a) (define (change_a number) (set! number_a number)) (change_a 1) number_a
では number_a は 1 を返すわけですが、この関数(change_a)を一般化して
(define (change_number character new) (set! character new))
としてどんな変数も任意の数に(数とは限りませんが)変えられるように試みました。しかし
(change_number number_a 2) number_a
はそのまま 1 を返します。set! をこのように使うことはできないのでしょうか? number_a ではなく character に 2 が代入されるならまだ理解できるのですが。
Shiro(2022/08/17 01:52:51 UTC): set!
は関数ではなく、最初に渡されるパラメータは評価されずにそのまま変数名として扱われます。2番めの
(define (change_number character new) (set! character new))
では、character
という変数自体に、new
の値をセットします。character
という変数の中身が元は何であったかは関係ありません。change_number
の呼び出し側で何を渡そうが動作は変わりません。
このcharacter""という変数は、仮引数として導入されたもので、関数
change_number"""の中だけで有効で、抜けたら消えてしまいます。
質問者さんは他のプログラミング言語は何かご存知ですか? Schemeに限らず、ほとんどのプログラミング言語(引数が値渡しである言語)では同じ動作になります。例えばCで書くとこうなります:
int number_a = 1; void change_number(int character, int newvalue) { character = newvalue; }
shimon(2022/08/07 09:44:54 UTC): 最近、Schemeでプログラムを書いてみたいと思い、手始めにcatコマンドなどの小さなプログラムを書いているのですが、標準入出力をバイナリポートとして扱う移植可能な方法が見当たりませんでした。例えば、current-input-port
はデフォルトの入力ポートを返しますが、R7RS smallの仕様ではこのポートはテキストポートとされています。Scheme処理系非依存かつプラットフォーム非依存(アプリケーション組み込みなどを想定せずにホストOS非依存ぐらいでもよい)な環境で標準入出力をバイナリポートとして扱う方法は存在するのでしょうか。標準はR7RS small + SRFIなどを想定しています。
GaucheやChibiなど一部のScheme処理系は処理系依存の仕様でテキストIOとバイナリIOを混在できることがあるようですが、例えばSagittariusは違うようです。
コード(qcat.scm):
(import (scheme base)) (define (cat iport) (let ((c (read-u8 iport))) (when (not (eof-object? c)) (write-u8 c) (flush-output-port) (cat iport)))) (cat (current-input-port))
実行(OS: Ubuntu 22.04, シェル: Zsh, Gauche: 0.9.10, Chibi: 0.9.1, Sagittarius: 0.9.8):
% echo foo | gosh -r7 qcat.scm foo % echo foo | chibi-scheme qcat.scm foo % echo foo | sagittarius -r7 qcat.scm Unhandled exception Condition components: 1. &assertion 2. &who get-u8 3. &message "binary-port" required, but got #<transcoded-port utf8-codec #<binary-input-port stdin>> 4. &irritants () 5. &stack-trace stack trace: [1] get-u8 [2] cat src: (read-u8 iport) "qcat.scm":4 [3] eval [4] main-loop [5] dynamic-wind [6] script
current-input-port
などが返すポートをバイナリポートとしても扱える処理系に限定して実装を進めてみたいと思います。ありがとうございました。
standard-input-port
とcurrent-input-port
を混ぜて使ったらどうなるかはわからない。
R7RSではいつかlargeで入れることになると思うんですが、まだ先だと思います。
standard-input-port
とcurrent-input-port
の話は、例えばCでいうとwrite
とfputs
(あるいはfdとFILE?)みたいなものでしょうか。個人的には、基本的な操作なので未規定な部分があるとしても欲しいなとも思いますが(素人考えですが、例えばwith-input-from-file
みたいなインターフェース)、textual/binaryが独立していない実装も許可されているようで実際そのような実装も少なくないようなので、現状手元ではbinary-port?
で標準入出力を事前に検査してみています。
いつもお世話になっています。 rfc.zlib を使って gzip で圧縮されたファイルを読もうとして、window-bits についてちょっと悩んでおり、ご教示いただけるとありがたく、、
Gauche のマニュアルから、window-bits の値は
8-15 | zlib |
24-31 | gzip |
40-47 | zlib/gzip 自動判定 |
を使うものと思って、
(port->string (open-inflating-port ip :window-bits 24))
としたところ、 ファイルサイズが 580 byte 前後以下の gz ファイルは読み込みできるのに対し、それ以上のサイズでは、
*** ZLIB-DATA-ERROR,IO-READ-ERROR: inflate error: incorrect header check
というエラーになってしまいました。 エラーメッセージでググっても、gz ファイルが不正、等しか見つけられず、、 それで、window-bits の値を変えてみてたところ、
<580 | 580< | |
16 | o | o |
24-27 | o | x |
28-31 | o | o |
32 | o | o |
40-43 | o | x |
44-47 | o | o |
という感じに。 Gauche のマニュアルからの予想では、24-31 と 40-47 では gz の展開OK、と思ってました。 zlib の挙動の詳細等わかっておらず、基本的に理解不足なのかと思いますが、どう考えたらいいんでしょうか。
If a compressed stream with a larger window size is given as input, inflate() will return with the error code Z_DATA_ERROR ...とありますね。ただ、となると、展開時には圧縮時に使用した window-bits は通常は知らされてないと思いますので、展開時の値としてこの条件を安全に満たすのは最大値(31 or 47)のみとなりますね。。
EmacsでGaucheをREPLで使っていての疑問です。
ある階層で.scmファイルをEmacsで開き、その中で
(include "a.scm")
とすると
*** ERROR: include file is not readable: "a.scm"
となります。
端末で gosh と打ち "gosh > " プロンプトを出して同様に行っても同様なメッセジが返ってきます。
しかしフルパスで指定すると、例えば
(include "/home/user/***/a.scm")
とすると読み込まれます。
一方端末でscmファイルを gosh ***.scm とすると、その中のincludeは相対パスでも読み込まれます。
REPLでは相対パスでincludeできないのでしょうか。
こんにちは。Makikiを扱っていての疑問です。 単純に
(print (srl:sxml->html `(sxml (html (p 1)))))
としますと
<sxml> <html> <p>1</p> </html> </sxml>
と返って来ますが、Makikiで
(define-http-handler "/" (lambda (req app) (respond/ok req '(sxml (html (p 1))))))
としますと「"SXML Error: \"sxml->html - unexpected type of node: \" 1"」となってしまうのは何故でしょうか。ご存知の方がいらっしゃいましたら、どうぞお願い致します。
sxml:sxml->html
を用いて変換されるという規則が書かれています。
つまり端的に言えば srl:sxml->html
と sxml:sxml->html
の挙動の違いであるということになります。(number->string 数値)と文字列に変換する一過程を加えることで表示できるようになりました。
たとえば random9.scm という名前で
(use srfi-27) (print (random-integer 10))
というファイルを作って gosh random9.scm を実行すると、0~9の乱数ではなく常に同じ数が返ってきます。
「これが噂の参照透過性か!」と思いましたがインタプリタで
(print (random-integer 10))
を評価すると
gosh> 乱数 #<undef>
と一回毎に違った数がランダムに返ってきます。(ランダムなので同じ数が返ってくることがありますが)
なので参照透過性などではなく単にメモリか環境下に入った値が返ってきているだけかもしれません。
これが当たっているか、そしてスクリプトでランダムを実現するにはどうすればいいか、ご存知の方がいらっしゃれば、お教えいただけませんでしょうか。
よろしくおねがいします。
(dotimes [10] (print (random-integer 10))
random-integer
を呼ぶ前に一回だけ(random-source-randomize! default-random-source)
を実行してください。
ありがとうございました。スクリプトを
(use srfi-27) (random-source-randomize! default-random-source) (print (random-integer 10))
としてみると、呼ぶ度ランダムな数が出るようになりました。
「(これはGauche特有の話ではなく、どんなプログラミング言語でもソフトウェア乱数発生器に共通の仕組みです)」とのことですので試しに
(use srfi-27) (print (random-integer 10)) (random-source-randomize! default-random-source) (print (random-integer 10))
と書き直してみると
gosh random9-2 8 0 gosh random9-2 8 1 gosh random9-2 8 8 ......
となり、「なるほど」となりました。
いつもお世話になってます。なにか勘違いしてたらすいません。(2022/01/10 01:16:19 UTC)
-p1
がつくケースにスクリプトが
対応してませんでした。ありがとうございます。
こんにちは、racketを勉強中なのですが、躓いてしまったのでどなたかご教授お願いしたいです。
(syntax-parse stx [(foo x) #:do [(define result (if (syntax-property #’x ’class) (if (x のプロパティが myid である) ’success1,#f) (if (x がクラス myid を持っている) ’success2,#f)))] #:when result (cond ((eq? result ’success1) パターンマッチ) ((eq? result ’success2) ’myid を付けた後, パターンマッチ)) ] [(foo y) #’’y])
現在このような方法でsyntax-parseのパターンマッチの機能改善を図っているのですが、現在もうどうして良いのかわからなくなってしまいました。本当に初心者なので、公式サイトで調べ回っている状態です。
quenzi: 個人のページから転記しました。
競技プログラミング(atCoder)で過去問にschemeで挑戦しているのですが、数を数える系が来ると大概処理時間オーバしてしまいます。
それ系はhash-tableを使えば行けるのですけど、これを自己を更新するやつ(!つき)なしでやるとすると、どういったアプローチがあるでしょうか?
例)
a b c d d a c c a e f a ...
それぞれ何個あるか教えてください
↓これだと話は早い
(hash-table-update! ht item (cut + 1 <>) 0))
ところが私は!付けないでやりたいと思っている。
よろしくお願いします。
(define (inc key ht) (hash-table-update! ht key (lambda (n) (+ n 1)) 0) ht)↓で良いかなと思いまいたが、処理が終わらなくなってしまい芳しくなかったです。単純な置き換えをしただけなのでほんとかどうかは怪しいですが。
(define (inc key immap) (imap-put immap key (+ 1 (imap-get immap key 0))))ともあれ、更新の仕方はどうあれhtと返すなら、使用感も変わりませんし、特にこだわるようなことでもなかったですね。m(__)m
mtmap です。連続ですみません。違う話題のため新しい項目を立てました。
gosh> (define a '("A" "B" "C")) a gosh> a ("A" "B" "C") gosh> (display a) (A B C)#<undef>
どうぞよろしくお願い致します。
gauche.record
をuseします。GaucheRefj:gauche.record。
Gauche専用になって良ければクラスの方が便利です。GaucheRefj:define-class。
Gaucheのオブジェクトシステムについてざっと知りたければGaucheRefj:オブジェクトシステムの紹介を。
|
で囲むことで使えます。
gosh> (define |Weird symbol| '|(what?)|) |Weird symbol| gosh> |Weird symbol| |(what?)|
display
はそういうものです。メッセージを表示したい時なんかに、文字列で組んだメッセージにいちいちダブルクオートがついたら変でしょう? そういった、ダブルクオートを表示したくない時のために使う手続きです。Schemeデータとして表示したい場合はwrite
を使ってください。
mtmap です。現在、自作の ruby プログラム(1000行ぐらい)を少しずつ gauche プログラムへ翻訳しながら、scheme の勉強を進めています。教えていただきたいことと、少しお願いがあり、発言させていただきます。
コード1:
(define (function x) (let ((variable 0) (long-name-function (lambda (x) (let ((long-name-variable (if (...) (begin ... ...) another-long-name-function))) ...)) ...))
コード2:
(define (function x) (let () (define variable 0) (define (long-name-function x) (let () (define long-name-variable (if (...) (begin ... ...) another-long-name-function)) ...)) ...))
どうぞよろしくお願い致します。
(let () ...)
はいりません。
internal defineは(define (function x) <この部分>)
でも使えます。
(define (function) (define a 0) (define (b x) ...) expr...) ≡ (define (function) (letrec* ((a 0) (b (lambda (x) ...))) expr...))
$
というマクロがあります。GaucheRefj:$
gosh <TAB> <手続き名>
と打てば直接リファレンスマニュアルに飛べます
practical-scheme.net/gauche/man/?l=jp&p=%s
(define variable (let () (define ...) ...))
数日前に define に関する頓珍漢な質問をしたものです。お蔭様で racket のソースコードは理解できました。
気を良くしていろいろ調べていたら、racket には pmap というライブラリがあることに気づき、私がやりたかったことはこれであっさり解決できることが分かりました(残念)。勉強になったので無駄ではありませんでしたが。。。
さて、Shiro さんにいろいろ教えていただいたので、gauche で該当する手続きを書いてみました。下記のソースコードで何か問題があれば御指摘お願いします。
どうぞしくお願い致します。
(use srfi.1) (use srfi.27) (use control.thread-pool) (use control.job) (use data.queue) (use gauche.process) (define (mtmap work lst no-of-workers) (let* ((pool (make-thread-pool no-of-workers)) (list-length (length lst)) (jobs (list->vector lst)) (results (make-vector list-length))) (for-each (lambda (j) (add-job! pool (lambda () (cons j (work (vector-ref jobs j)))) #t #f)) (iota list-length)) (wait-all pool) (for-each (lambda (j) (let ((result (job-result j))) (vector-set! results (car result) (cdr result)))) (dequeue-all! (thread-pool-results pool))) (vector->list results))) ;;; TEST mtmap (define work (lambda (j) (let ((t (random-integer 20))) (run-process `(sleep ,t) :wait #t) (display (list j t)) (newline) t))) (display (mtmap work '(A B C D E F G H) 4)) (newline)
control.mapper
では「既存のスレッドプールで実行する」か、
「引数のリストをスレッド数に分割して固定的に仕事を振ってしまう」かを選べるようになってます。
jobs
というベクタ経由にしなくても、例えばこれでいけます:
(for-each-with-index (lambda (j e) (add-job! pool (lambda () (cons j (work e))))) lst)
sys-available-processors
で取れます。
(lambda ...)
を渡すのが正解です。メモリ上の(環境を含めた)クロージャがワーカースレッドと共有されます。
#!token
を使って、ファイル単位で宣言的にどのreadtableを使うか指定させるなら
まだいけますが、その場合も指定のreadtableをどこから読んでくるかとかいくつか
面倒な問題があります。
Scheme に興味を持って勉強を始めました。全くの初歩的な質問ですみません。
いろいろな処理系を触って見たのですが、REPL での define の挙動が違っていることに気づきました。
例えば、
(define a 1)
とすると racket/chicken/guile/gambit では何も表示せずに次のプロンプトが現れますが、gauche では "a" が表示されます。
続いて、これは蛇足に近いですが、
(display (define a 1))
とすると racket/chicken/guile/gambit ではエラーが表示されますが、gauche では "a#<undef>" が表示されます。
これは racket のあるプログラムで、define を REPL の出力(上記の最初の例では"1"、実際は何かを評価している)を抑制するためだけに使用している(と思われる)例を見て気づきました。定義された識別子(シンボル?)は他で一切使われおらず、説明もないのでそう推論しました(初心者の私にとって気づくのにとても時間がかかりました)。
以下の3点の質問をさせてください。
1) REPL の出力を抑制するために define を用いるのは scheme の一般的な方法でしょうか?
2) もしそうでないとしたら、(gauche を含めて) REPL の出力を抑制する一般的な方法があれば教えてください(初心者にとって出力抑制に define を用いるのは自然には思えないのです)。
3) (define a 1) が値を返すのは合理的に見えるのですが、何か深い理由があるようでしたら教えてください。
どうぞよろしくお願いいたします。
define
の「返り値」は規定されていないので、何を返すかは処理系の自由です。そもそも、規格を厳密に読むと(define ...)
は「式」ではないので、返り値を使おうとすること自体が間違いだからエラーにする、という選択も可能です。そのへん、Schemeでは規格で決めずにおいて、各処理系の裁量に任せるという部分がたくさんあります。ちなみにGaucheで変数名を返しているのは特に深い考えがあるわけじゃなく、REPLで何か打ち込んだら反応が欲しいからとりあえず返しておくか、程度のことです。
define
」というのは特定の処理系のみで使える話です。
(my-expression)
を評価したいけどプロンプトに戻り値を出したくなければ、(begin (my-expression) (values))
とします。ただまあ、ちょっとまどろっこしいですね。
Welcome to Racket v6.11. > (begin (+ 1 2) (values)) >
let
の本体内では
まず定義の並び、それから式の並び、としなければならないのでそれに揃えたか)、かなあと思います。
継続を使って関数を作ってみようと思っています。例として2つの引数を取ってその足し算をする関数、add を書いてみました
(define stack) (define (pre-add a) (+ a (call/cc (lambda (cc) (set! stack cc) 1))))
として(多分、1 でも何でも数ならこの場合良いはず) stack を「次に(pre-add に渡した引数の)数値を足す手続き」としてみました。
gosh> (pre-add 3) 4 gosh> (stack 4) 7
なので add 関数は
(define (add a b) (pre-add a) (stack b))
でいいと思い
(add 3 4)
としてみると何も返ってこずgoshが止まらなくなります。何故なのでしょうか。
pre-add
をREPLで直接実行した場合、stackに入る継続は
(lambda (x) (+ a x))だけですが(継続に渡した引数xが、call/ccから戻り値として返ってくる形)、
add
手続きにまとめた場合、継続にはその後にくる(stack b)
の計算も含まれてしまうのです。
(lambda (x) (+ a x) ;; pre-addの残り (stack b)) ;; その後の計算
stack
を呼び出すと、それはまたxから戻ってくる形になるので、再びstack
が呼び出され…と無限ループになります。
(define (add a b) (+ a (call/cc (lambda (cc) (set! stack cc) 1))) (stack b))こうなってしまい
stack = (+ a [x] (stack b)) = (+ a [x] (+ a [x] (stack b)) b) = (+ a [x] (+ a [x] (+ a [x] (stack b)) b) b) ...と無限ループになるという理解で合っていますでしょうか。
(define rei (+ a))
これに「aは不束縛変数だ」とエラーになるのは分かるのですが
(define (rei) (+ a))
は #<closure (rei)> となるのは何故でしょうか。 またこれを
(rei) ((rei) 1) (rei 1)
とするとやはり「aは不束縛変数だ」と「引数の数が違う」とエラーが返ってくるのですが、この関数(?)はどうやって使うのでしょうか?
(define (rei) ...)
とカッコで囲むと、rei
という0引数の関数を定義したことになります。関数の中身は、それを呼び出すまでは評価されません。 (でないと、例えば相互再帰する関数を定義するのがややこしくなります)。
(rei)
と書くと、これはrei
を関数として引数なしで呼び出すことになります。このときはじめてa
の値を取りにゆき、まだ束縛されてないのでエラーが出ます。(define a 2)
などとしてa
を定義してからもう一度呼び出せば2が帰ってくるはずです。
(rei 1)
というのは、1を引数として関数rei
を呼び出す式です。rei
は引数を取らない関数なので、「引数の数が違う」というエラーになります。
(rei)
というのは他のプログラミング言語で rei()
と書くのと同じです。(rei 1)
は他のプログラミング言語でrei(1)
に当たります。((rei) 1)
は rei()(1)
です。(define (rei) (+ a))
は、例えばJavascriptなら function rei() { return +a; }
と書くのと同じです。
ご報告だけですが、Boxes や 改善の String%20cursors (同様の複数)をクリックすると、Error になってしまいます。Boxes は英語のページではなりません。
Boxes Error couldn't open input file: "gauche-refe-draft/Index-_002d-Bian-Shu-Suo-Yin-.html": No such file or directory String%20cursors Error wrong number of arguments for #<closure (search-from-toc lang draft? name)> (required 3, got 2)
,pa
と打ってください。直前の結果が省略なしで表示されます。,pm
トップレベルコマンドが使えます。,help pm
でヘルプが出ます。
単に使おうとすると、 *** ERROR: unbound variable: port-position となってしまいます。 (use srfi-192) がいるのかとも思いましたが、srfi-192.scm は見当たらず、Gauche に組み込まれてるはず?とも、悩んでます。
port-position
もsrfi-192.scmも0.9.10に入っています。もしかして古いバージョンのGaucheではありませんか?
ERROR:invalid application: (#<syntax null#and> #t #t) となってしまいます。
(fold + 0 '(1 2)) => 3 と同様のことをしているつもりなのですけども。
and
は関数ではないので、引数として渡すことはできません。Gaucheではたまたまand
を評価すると#<syntax null#and>
という値らしきものが返ってきますが、本来and
は単独で評価値を持つものではなく、(and ...)
という形式で使われた場合のみ意味を持ちます。これはif
やlambda
も同じです。
(and #f (destroy-the-universe))
では、最初の引数が#f
なので(destroy-the-universe)
は決して呼ばれません。しかしたとえば (define (apply-2 fn a b) (fn a b))
とかして (apply-2 and #f (destroy-the-universe))
を許すと、and
が見る前に引数が評価されて宇宙が破壊されてしまいます。つまり意味が変わっちゃうんですね。
それを「使う方が気をつけて使えば良い」とする設計もまあ方針としてはあり得るんですが、
(apply-2 fn a b) ≡ (fn a b)
と定義したということは
(apply-2 fn a b)
は(fn a b)
と常に置換可能である、ということだ、
とする方が一貫性がありますね。and
が渡せるようにするとその前提を壊してしまうわけです。
(all-modules)
は、現在ロード済みのモジュールのリストを返す関数だと認識しています。
gosh を起動直後に、(all-modules)
を呼ぶと、#<module util.match>
がリストに含まれていたので
util.match モジュールはデフォルトでロードされるのかと思ったのですが、
実際に match
マクロを呼ぶと unbound variable になります。
質問ですが、(all-modules)
はロードされていないモジュールも含まれるということなのでしょうか?
(all-modules)
のドキュメントには現在存在する名前付きモジュールのリストを返すと記載されており、存在の意味を掴みかねています。
use
は「require
(モジュールがロードされていなければロードする)+import
(現在のモジュールからそれを見えるようにする)」という動作です。例えばあるモジュールが他のモジュールにuse
されていたためにロードされれた、という場合なら、現在のモジュールからは見えないかもしれません。
なおR7RSのimport
はGaucheのuse
と機能的にほぼ同じです。
util.match
についてはちょっと特殊事情があって、Gauche自身で書かれた組み込み関数はコンパイル時にmatch
マクロで展開されてるものがあり、その時にutil.match
モジュールへの参照を含む識別子が作られちゃってるんですね。(衛生的マクロのために、マクロ展開後の式は各識別子がどのモジュールのものを指しているかという情報を保持しています)。そのため、Gauche初期化時に名前空間を示すためだけのutil.match
モジュールが作られていて、「ロードされていないのにモジュールが存在する」という状態が生じています。これはバグなのでfixします。
util.match
はロードされているので、(all-modules)
に現れるのは問題ありません。match
が見えないのは単にuserモジュールがimportしてないだけです。gosh -q
で起動すると外部Schemeファイルを初期化時に一切ロードしないので、その状態で(all-modules)
にutil.match
が現れるのがバグです。
(define (aaa) (define (tasu x y) (+ x y)))
あるいは
(define (aaa) (define (tasu x y) (+ x y)) 'done)
としたとき、関数tasuを実行するにはどうしたら良いでしょうか。
(define bbb) (define (qwe) (define (tasu a b) (+ a b)) (set! bbb tasu) 'done)
とすると
gosh> (qwe) done gosh> (bbb 1 2) 3
となるのですが
gosh> (tasu 1 2) *** ERROR: unbound variable: tasu
となるのは何故でしょうか。何となく
((aaa) tasu 1 2) とか (begin (aaa) (tasu 1 2))
などで実行する方法がありそうな気がするのですが、分かりません。
(define (aaa) (define (tasu x y) (+ x y)) ...)というのは、次のコードと同じです。
(define (aaa) (define tasu (lambda (x y) (+ x y))) ...)つまり、tasuというローカル変数に、
(lambda (x y) (+ x y))
という無名関数を束縛しています。
aaa
のスコープを抜けてしまったら、tasu
という変数は見えなくなります。
(set! bbb tasu)
とした場合、tasu
に束縛されていた無名関数がグローバル変数bbb
にも束縛されることになるので、aaa
のスコープを抜けた後でもbbb
から呼び出すことが出来ます。
aaa
の中で作ったローカル変数をaaa
の戻り値にしています:
(define (aaa) (define (tasu x y) (+ x y)) tasu)
#<closure ...>
というオブジェクトが返ってきます。それが「関数」の本体です。(表示されている (aaa tasu)
というのは単なるデバッグ情報で、aaa
の中でtasu
という名前で定義されたことを示していますが、tasu
という名前自体は関数にとって特別に意味があるものではありません。)
gosh> (aaa) #<closure ((aaa tasu) x y)>
gosh> (define bbb (aaa)) bbb gosh> (bbb 1 2) 3
(define (add-n n) (define (tasu x) (+ n x)) tasu)
aaa
とよく似ていますが、add-n
はひとつ引数n
を取ります。そして、
tasu
は二つではなく一つだけ引数を取って、add-n
の引数であるn
と足した値を返します。add-n
はその関数内関数tasu
を返り値とします。
add-n
" を呼んで作られた関数内関数は、その時のn
を覚えておいてくれるんです。
gosh> (define add-1 (add-n 1)) add-1 gosh> (add-1 2) 3 gosh> (add-1 3) 4 gosh> (define add-10 (add-n 10)) add-10 gosh> (add-10 2) 12 gosh> (add-10 3) 13
add-1
、add-10
はそれぞれ、add-n
に渡された引数1と10を覚えていて、それに渡された引数を足して返す関数になっています。
Common Lisp だと (atom list) とよく書いていたのですが、Schemeにはatom相当の手続きは無いのでしょうか?
atom
はもともとごく初期のLispにおいて、データ型にコンスセルとシンボルしか無かった時代に作られたものです。二種類しかないので、「コンスセルでないもの」をatom
で調べることに意味がありました。しかしそれから数値、文字列、ベクタなどデータ型が増えてゆくにつれ、atomであることだけ分かってもあまり意味がなくなりました。「コンスセルでないこと」を調べたいなら(not (consp x))
とすれば良いし、そうでなければsymbolp
、numberp
などで細かく調べる必要があることがほとんどだからです。
Common Lispにはおそらく互換性からatom
が残りましたが、Schemeでは削られました。(not (pair? x))
を使うのが良いでしょう。
atom
はそのコンストラクタです( https://practical-scheme.net/gauche/man/?l=jp&p=atom )。これはClojureから採り入れました ( https://clojure.org/reference/atoms )。
atom
は古のLispだったのか。ありがとうございました。
gauche-gtk2のgtkglやglgdをインストールしましたが動きません。
gauche-gtkなどはdynamic-loadできるのですがgauche-gtkglやgauche-glgdはdynamic-loadするとfailed to linkと出てしまいます。
どうやら_gdk_gl_config_get_depthなどのシンボルがないとのとのことで
どこを直せばいいでしょうか?
error:
%make check cd src; /Applications/Xcode.app/Contents/Developer/usr/bin/make all make[1]: Nothing to be done for `all'. cd lib; /Applications/Xcode.app/Contents/Developer/usr/bin/make all make[1]: Nothing to be done for `all'. if test Xgauche-gtkgl.so != X; then (cd gtkgl; /Applications/Xcode.app/Contents/Developer/usr/bin/make all); fi make[1]: Nothing to be done for `all'. if test X != X; then (cd glgd; /Applications/Xcode.app/Contents/Developer/usr/bin/make all); fi cd src; /Applications/Xcode.app/Contents/Developer/usr/bin/make check /usr/local/bin/gosh -I. -I../lib test-gdk.scm >> test.log Testing Gdk ... 2020-08-08 20:30:21.086 gosh[24509:139763] *** WARNING: Method userSpaceScaleFactor in class NSView is deprecated on 10.7 and later. It should not be used in new applications. Use convertRectToBacking: instead. failed. discrepancies found. Errors are: test gdk-cursor: expects 2 => got -1 test gdk-visual: expects (24) => got (1 24 32) test gdk-visual: expects (4) => got (0 4) /usr/local/bin/gosh -I. -I../lib test-gtk.scm >> test.log Testing Gtk ... 2020-08-08 20:30:21.302 gosh[24511:139846] *** WARNING: Method userSpaceScaleFactor in class NSView is deprecated on 10.7 and later. It should not be used in new applications. Use convertRectToBacking: instead. failed. discrepancies found. Errors are: test callback: expects (#t #f) => got (#t #t) test destroy -> GC: expects #f => got #<<gtk-button> 0x10441bdb0> test destroy -> GC (cyclic): expects (#f #f) => got (#<<gtk-button> 0x10442a9c0> #<closure ((#f cb) . _)>) test destroy widget tree: expects (#f #f) => got (#<<gtk-button> 0x1044231b0> #<<gtk-button> 0x104423180>) if test Xgauche-gtkgl.so != X; then (cd gtkgl; /Applications/Xcode.app/Contents/Developer/usr/bin/make check); fi /usr/local/bin/gosh -I. -I../src -I../lib test.scm >> test.log Testing GtkGLExt ... *** ERROR: failed to link ./gauche-gtkgl.so dynamically: dlopen(./gauche-gtkgl.so, 10): Symbol not found: _gdk_gl_config_get_depth Referenced from: ./gauche-gtkgl.so Expected in: flat namespace in ./gau ... While loading "../lib/gtk/gtkgl.scm" at line 23 While compiling "./test.scm" at line 4: (use gtk.gtkgl) While loading "./test.scm" at line 4 Stack Trace: _______________________________________ make[1]: *** [check] Error 70 make: *** [check] Error 2
gdk_gl_config_get_depth
が無いと言っているので、バージョンが変わって以前あったAPIがなくなってしまったように見えます。
これはgauche-gtkglをアップデートしないとならないですね。
私はもう長らく使ってないのでどこまで手を入れる必要があるか見当がつかないですが、
とりあえずこの関数についてだけなら、gtkgl/gdkgllib.stub
の次の箇所で呼んでいるので:
(define-cproc gdk-gl-config-get-depth (glconfig::<gdk-gl-config>) (call <int> "gdk_gl_config_get_depth"))
gdk_gl_config_get_depth
のところを新しい名前にする
% brew info gtkglext gtkglext: stable 1.2.0 (bottled) OpenGL extension to GTK+ https://projects.gnome.org/gtkglext/ /usr/local/Cellar/gtkglext/1.2.0_3 (78 files, 2MB) * Poured from bottle on 2020-08-08 at 18:26:35 From: https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/gtkglext.rb ==> Dependencies Build: pkg-config ✔ Required: glib ✔, gtk+ ✔ ==> Analytics install: 814 (30 days), 2,401 (90 days), 9,782 (365 days) install-on-request: 96 (30 days), 193 (90 days), 786 (365 days) build-error: 0 (30 days)
% mdfind -name gtkglext1.0.pc|grep /usr/local/Cellar /usr/local/Cellar/gtkglext/1.2.0_3/lib/pkgconfig/gtkglext-1.0.pc % export PKG_CONFIG_PATH=/usr/local/Cellar/gtkglext/1.2.0_3/lib/pkgconfig % echo $PKG_CONFIG_PATH /usr/local/Cellar/gtkglext/1.2.0_3/lib/pkgconfig (エラーはおなじです)
ところで初めにpkg-configで以下のようにでたんですが、これってもとからgtkglext-1.0.pcがみつかっていたということですかね?
% pkg-config --libs-only-L gtkglext-1.0 -L/usr/local/Cellar/gtkglext/1.2.0_3/lib -L/usr/local/Cellar/gtk+/2.24.32_3/lib -L/usr/local/Cellar/glib/2.64.4_2/lib -L/usr/local/Cellar/gtk+/2.24.32_3/lib -L/usr/local/Cellar/pango/1.44.7/lib -L/usr/local/Cellar/harfbuzz/2.7.0/lib -L/usr/local/Cellar/atk/2.36.0/lib -L/usr/local/Cellar/cairo/1.16.0_3/lib -L/usr/local/Cellar/gdk-pixbuf/2.40.0_1/lib -L/usr/local/Cellar/glib/2.64.4_2/lib -L/usr/local/opt/gettext/lib
gosh> (dynamic-load "./gauche-gtkgl.so") *** ERROR: failed to link ./gauche-gtkgl.so dynamically: dlopen(./gauche-gtkgl.so, 10): Symbol not found: _Scm_GObjectClass Referenced from: ./gauche-gtkgl.so Expected in: flat namespace in ./gauche-gtk ... Stack Trace: _______________________________________ 0 (eval expr env) at "/usr/local/Cellar/gauche/0.9.9/share/gauche-0.97/0.9.9/lib/gauche/interactive.scm":269
lib/gtk/gtkgl.scm
を見てもらうとわかるんですが、(use gtk)
してからdynamic-load
してますよね。(use gtk)
の段階でgauche-gtk.soがロードされます。
もし既に(use gtk)
してるにもかかわらずそのエラーが出たとしたら、
OSXでのビルドが何かおかしなことになっているっぽいですね。こちらでも時間が出来たら検証してみます。
(use gtk)
するとでなくなりましたね。gint gdk_gl_config_get_depth (GdkGLConfig *glconfig);とあるんですけどね・・・
otool -L gauche-gtkgl.so
ってやってみてください。リンクされてるlibgtkglext.dylibのパスが出ると思います。それがbrewで入れたやつと合っているか…?
libgtkglext-quartz-1.0.0.dylib
あたりですかね。
Gauche-gtk2/gtkgl % otool -L gauche-gtkgl.so gauche-gtkgl.so: /usr/local/opt/gtkglext/lib/libgtkglext-quartz-1.0.0.dylib (compatibility version 201.0.0, current version 201.0.0) /usr/local/opt/gtkglext/lib/libgdkglext-quartz-1.0.0.dylib (compatibility version 201.0.0, current version 201.0.0) /usr/local/opt/gtk+/lib/libgdk-quartz-2.0.0.dylib (compatibility version 2401.0.0, current version 2401.32.0) /usr/local/opt/glib/lib/libgmodule-2.0.0.dylib (compatibility version 6401.0.0, current version 6401.4.0) /usr/local/opt/gtk+/lib/libgtk-quartz-2.0.0.dylib (compatibility version 2401.0.0, current version 2401.32.0) /usr/local/opt/pango/lib/libpangocairo-1.0.0.dylib (compatibility version 4401.0.0, current version 4401.7.0) /usr/local/opt/pango/lib/libpango-1.0.0.dylib (compatibility version 4401.0.0, current version 4401.7.0) /usr/local/opt/harfbuzz/lib/libharfbuzz.0.dylib (compatibility version 20700.0.0, current version 20700.0.0) /usr/local/opt/atk/lib/libatk-1.0.0.dylib (compatibility version 23610.0.0, current version 23610.1.0) /usr/local/opt/cairo/lib/libcairo.2.dylib (compatibility version 11603.0.0, current version 11603.0.0) /usr/local/opt/gdk-pixbuf/lib/libgdk_pixbuf-2.0.0.dylib (compatibility version 4001.0.0, current version 4001.0.0) /usr/local/opt/glib/lib/libgio-2.0.0.dylib (compatibility version 6401.0.0, current version 6401.4.0) /usr/local/opt/glib/lib/libgobject-2.0.0.dylib (compatibility version 6401.0.0, current version 6401.4.0) /usr/local/opt/glib/lib/libglib-2.0.0.dylib (compatibility version 6401.0.0, current version 6401.4.0) /usr/local/opt/gettext/lib/libintl.8.dylib (compatibility version 11.0.0, current version 11.0.0) /usr/local/opt/gauche/lib/gauche-0.97/0.9.9/x86_64-apple-darwin19.2.0/libgauche-0.97.dylib (compatibility version 0.0.0, current version 0.0.0) /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1281.100.1) /opt/X11/lib/libSM.6.dylib (compatibility version 7.0.0, current version 7.1.0) /opt/X11/lib/libICE.6.dylib (compatibility version 10.0.0, current version 10.0.0) /opt/X11/lib/libXext.6.dylib (compatibility version 11.0.0, current version 11.0.0) /opt/X11/lib/libX11.6.dylib (compatibility version 10.0.0, current version 10.0.0)
nm /usr/local/opt/gtkglext/lib/libgdkglext-quartz-1.0.0.dylib
の出力にgdk_gl_config_get_depth
出てきます?
gdk_gl_config_get_*
は
あるのに_get_depth
が無いですね。_get_screen
も無いか。これ以上はBrewのビルドがどうなってるか調べないと何ともいえないです。
C言語でGaucheのリストを作って、Gaucheで書いたプログラムで操作できないか考えています。
具体的には、C言語プログラムでGaucheのリスト構造(データ)作る → Gaucheのプログラムを別途用意して、そのプログラムを適用する → 結果をC言語プログラムで受け取る
のような流れを考えています。
SCM_MAKE_INT()をはじめ、GaucheオブジェクトをC言語から作成できるようなので、それらを使おうと思います。 ただ、以下のような点にひっかかっているのと、そもそも、どこかに基本となるオブジェクトの生成や、リストを作成する関数の一覧があれば、助かります。
ひっかかっている点
よろしくお願いします。 (umehara)
make gauche-deve.info
とするとinfoファイルができます。まだ書いてないところが多いですが、数値については書いてあります。
umehara (レベル3): ご丁寧に御回答ありがとうございます。返事遅くなってすみませんでした。
Web上でC言語からGuacheを使うための情報をさがすと、とあるページで、SCM_MAKE_INTを使っていたので、それに対応する浮動小数点をつくる関数・マクロをGaucheのヘッダからさがしました。SCM_MAKE_FLONUMのようなものが見つからず、一方で、Scm_MakeFlonumがみつかって、なぜ名前に一貫がないか(マクロと関数混在しているのか)と思い始め、わからなくなり質問させていただいた次第です。doc参照できました。C APIに関して探していた情報がありました。ありがとうございます!
※ 後から参照する方のために
以下、自分自身の環境でドキュメン出力で少し修正した点です。 doc以下で make gauche-deve.info する時に、うまく作れなかったので、以下のようにしました。まず、makeinfoコマンドがなかったので sudo apt install texinfo でいれました。makeinfoコマンドはtexinfoパッケージの中にあるようでした。また、doc 以下のMakefile内の、MAKEINFO に対する値が空欄になっていたので、 'MAKEINFO = makeinfo' とエディタで修正しました。 なお、生成された .infoファイルの読み方は、Emacsを開いて、'C-u C-h i' で読めました。
※ (追記:umehara) 以下のコードで数値のリストをGaucheに渡して、その計算結果を、Cで表示できました
同じディレクトリに、c_with_gauche.c と my-calc.scm を作って、後述のようにコンパイル・実行しました。Gauche自体はユーザ領域にインストールしており、共有ライブラリがダイナミックライブラリのサーチパスに入っていなかったので、実行前に追加などしています。
#include <stdio.h> #include <gauche.h> int main() { GC_INIT(); Scm_Init(GAUCHE_SIGNATURE); ScmObj elem1 = Scm_MakeInteger(1); ScmObj elem2 = Scm_MakeInteger(2); ScmObj elem3 = Scm_MakeInteger(3); ScmObj elem4 = Scm_MakeInteger(4); ScmObj scm_lis_obj = SCM_LIST1( Scm_Cons(elem1, Scm_Cons( elem2, Scm_Cons( elem3, Scm_Cons(elem4,SCM_NIL))))); Scm_AddLoadPath(".", 0); ScmLoadPacket lpak; Scm_Load("my-calc.scm", 0, &lpak); static ScmObj my_proc = SCM_UNDEFINED; // static is specified following Shiro-san's reply. SCM_BIND_PROC( my_proc, "my-sum", Scm_CurrentModule()); ScmObj result = Scm_ApplyRec( my_proc, scm_lis_obj); if(SCM_INTEGERP(result)){ printf("The result is %ld.\n", Scm_GetInteger(result)); } printf("Finished\n"); return 0; }
(define (my-sum lis) (print lis) (if (null? lis) 0 (+ (car lis) (my-sum (cdr lis))) ) )
gcc c_with_gauche.c $(gauche-config -I) $(gauche-config -L) $(gauche-config -l)
libpath=$(gauche-config -L | xargs) export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:${libpath:2} ./a.out
(1 2 3 4) (2 3 4) (3 4) (4) () The result is 10. Finished
Documentation: No (requires makeinfo and gzip to build documents)
と表示されるようになっています。後からmakeinfoを入れたなら、もういちどconfigureを走らせればmakefileを手編集する必要はありません。
my_proc
はstatic宣言してください。SCM_BIND_PROCはmy_proc
がSCM_UNDEFINED
だった時にScheme手続きを探しにいって、
結果をmy_proc
にストアします。2回目以降は探しにゆく必要がないわけです。
fprintf
と同じように使える
Scm_Printf
が便利です。最初の引数にはFILE*
ではなくScmPort*
を
取りますが、SCM_CUROUT
(current-output-port) や SCM_CURERR
(current-error-port) が使えます。第二引数はprintf
と同じフォーマット文字列ですが拡張されてて、
%S
や%A
でSchemeオブジェクトを表示できます。%S
はwrite
表示、
%A
はdisplay
表示です。
Scm_Printf(SCM_CUROUT, "The result is %S.\n", result);
普通の再帰を末尾再帰に書き換えること、そして末尾再帰(定義中で iter などで変数を加え、さらにその変数に初期値を与えるもの)を普通の再帰に書き換えることは、常に可能なのでしょうか?
Zコンビネータは
Z = λf.(λx.f(λy.(xxy))) (λx.f(λy.(xxy)))
なわけで、(Z g) は
(Z g) = λx.g(λy.xxy)(λy.g(xxy)) = g(λy.((λx.g(λy.xxy))(λx.g(λy.xxy))y) = g(λy.(Z g) y)
となるのでSchemeで書くと
(define (Z g) (g (lambda (y) ((Z g) y))))
引数の g をラムダにして
(define Z (lambda (g) (g (lambda (y) ((Z g) y)))))
としたものはZコンビネータと呼べるのでしょうか。Zの定義の中にZが現れているのでそもそもの「匿名関数で再帰を実現」ではないような気がします。
しかしたとえば階乗計算用に fac
(define fac (lambda (f) (lambda (n) (if (= n 1) 1 (* n (f (- n 1)))))))
を用意してZに食わせると
gosh> ((Z fac) 3) 6
とfac の定義自体には fac を使わずに再帰ができているので、Zコンビネータ自体にZが現れていても「再帰させたい関数の名前を使わずに再帰させる」は実現できているので、Zコンビネータでいい気もします。
定義に自由変数(この場合Z)が含まれているのでコンビネータの定義上コンビネータではないのかもしれませんが……
(set! Z なんとか)
とした場合に動作が変わっちゃったら誤魔化してることがバレちゃいますから、どのへんまでの操作を前提とするかによってコンビネータと呼ぶべきかどうかは変わって来ると思います。
Gauche-al http://www.koguro.net/prog/Gauche-al/Gauche-al-20051009.tar.gz からダウンロードしてきて
./configure make
するとエラーで
clang: error: no such file or directory: 'al_head.c'
src/ に空のac_head.cを作成してみたところ
./gauche-al.h:40:10: fatal error: 'AL/al.h' file not found
とまたエラーが出てしまいます。
https://github.com/Hamayama/Gauche-al-mg では
chmod +x DIST ./DIST ./configure make
すると
./gauche-al.h:40:10: fatal error: 'AL/al.h' file not found
だけで済みます。 おそらく、configure の
3685 for ac_header in OpenAL/al.h 3686 do : 3687 ac_fn_c_check_header_mongrel "$LINENO" "OpenAL/al.h" "ac_cv_header_OpenAL_al_h" "$ac_includes_default" 3688 if test "x$ac_cv_header_OpenAL_al_h" = xyes; then : 3689 cat >>confdefs.h <<_ACEOF 3690 #define HAVE_OPENAL_AL_H 1 3691 _ACEOF 3692 3693 case $target in 3694 powerpc-apple-darwin*) 3695 $as_echo "#define MacOSX 1" >>confdefs.h 3696 3697 AL_LIBS='-framework OpenAL' 3698 ;; 3699 *) 3700 ;; 3701 esac 3702 3703 fi 3704 3705 done
あたりがうまく行っていないようで gauche-al.h の
35 #if MacOSX 36 #include <OpenAL/al.h> 37 #include <OpenAL/alc.h> 38 #include <OpenAL/alut.h> 39 #else 40 #include <AL/al.h> 41 #include <AL/alc.h> 42 #include <AL/alut.h> 43 #endif
でelse以降を展開してしまっているようです
./configure の結果では、OSXと認識されてるっぽいのですが・・・
./configure ... checking build system type... x86_64-apple-darwin19.4.0 checking host system type... x86_64-apple-darwin19.4.0 checking target system type... x86_64-apple-darwin19.4.0 ....
どのようにすれば良いですか?
powerpc-apple-darwin*
になってるので、x86_64-...
だと飛ばされちゃってるんだと思います。
本来はconfigure.acの方を直さないとなりませんが、とりあえずインストールするだけなら、
configureの該当個所を *-apple-darwin*
に変えてやってみてください。
al-lib.c:817:8: error: use of undeclared identifier 'AL_DATA' case AL_DATA: al-lib.c:1569:82: error: use of undeclared identifier 'AL_SOURCE_ABSOLUTE' Scm_MakeBinding(SCM_MODULE(mod), SCM_SYMBOL(scm__rc.d1310[5]), Scm_MakeInteger(AL_SOURCE_A... ^ al-lib.c:1668:83: error: use of undeclared identifier 'AL_DATA' Scm_MakeBinding(SCM_MODULE(mod), SCM_SYMBOL(scm__rc.d1310[38]), Scm_MakeInteger(AL_DATA), ...al-lib.stub には (define-enum AL_DATA) のように定義されてるんですが、うーん、わかりません。アドバイスお願いします。
define-enum
は既にあるCの定数をScheme側に見せるものです)。
Scheme側でそれらの定数を使っていないなら、define-enumをコメントアウトすればコンパイルは通ると思います。
case AL_DATA
のところは別途対応しないとだめか。OpenAL側の変更を追わないとわからないですね。名前が変わっただけかもしれない。
tar -zxvf Gauche-al-20051009.tar.gz cd Gauche-al-20051009エディタで configure.ac を開いて powerpc-apple-darwin* を apple-darwin* に変更
cd src touch al_head.c al_tail.c cd .. autoconf ./configureエディタで al-lib.stubを開いて
45 (define-enum AL_SOURCE_ABSOLUTE) ... 78 (define-enum AL_DATA) ... 140 case AL_DATA:を↓に変更
45 ;;(define-enum AL_SOURCE_ABSOLUTE) ... 78 ;;(define-enum AL_DATA) ... 140 /* case AL_DATA: */そしたら
make make install最後に確認
cd example gosh play.scmすると「ジャラーン」と鳴る
ちなみにですが、Gauche-gl ( http://practical-scheme.net/gauche/packages.html )
はバッチリでした!
環境 OS:Mac OSX Catalina 10.15.4
よろしくお願いします。
追記 Gauche-glについてなんですが、doc/ の Makefile で texi2html -number の -number がエラー
Option number is ambiguous (number-footnotes, number-sections) 詳しくは `texi2html --help' を実行してください。 make: *** [Makefile:52: gauche-gl-refe.html] Error 2
だったので
texi2html gauche-gl-refe.texi
としたら行けましたがどうでしょうか?
(改行がうまくできずすみません)
「Yコンビネータの斜め下」にてSiro氏に説明していただいた「正格評価」「非正格評価」を知ろうとしWikipediaの「評価戦略」のページを読んでみましたが何が何やらでしたので、「Scheme:lambdaだけで再帰」のコードを自分なりに分かる範囲で変えてみて理解してみようとしました。
まず
上記waの部分を(X X)に直接「書き換える」かわりに、 引数waに (lambda (x) ((X X) x)) という関数を渡してやれば、 (wa (- n 1)) の部分が ((lambda (x) ((X X) x)) (- n 1))、すなわち ((X X) (- n 1))になります。
の部分がおそらくη変換なのだろうと推測し、(wa)に(X X)を渡すようにしてみました。名前を make-wa3Y に変えています。
(define make-wa3Y (lambda (X) [(lambda (wa) (lambda (n) (if (<= n 0) 0 (+ n (wa (- n 1)))))) (X X)]))
で、これは結局 make-wa2
(define make-wa2 (lambda (X) (lambda (n) (if (<= n 0) 0 (+ n ((X X) (- n 1)))))))
と同じことになるので
(make-wa3Y make-wa3Y) ≡ (make-wa2 make-wa2)
なので行けると思いましたが、
(make-wa3Y make-wa3Y)
は止まりませんでした。 何故止まらないのか? 言い換えると (make-wa2 make-wa2) は何故止まるのかを考えてみました。
まずmake-wa3Yをラムダ式にしてみますと λX.(λwa.λn.F (X X)) (ただしF = [n + wa(n-1) ※ただしn<=0ならば0]) なので(make-wa3Y make-wa3Y)は
λX.(λwa.λn.F (X X)) λX.(λwa.λn.F (X X)) =λX.(λn.G)λX.(λn.G) (ただしG = [n + (X X)(n-1) ※ただしn<=0ならば0] =λn.[n + {(λX.(λn.F) λX.(λn.F)}(n-1) ※〜] =λn.[n + (make-wa2 make-wa2)(n-1) ※〜]
で (make-wa2 make-wa2) と同じになって止まるはずではと思えました。 では何が違うのか考えてみますと make-wa2 = λX.λn.G が (make-wa3Y make-wa3Y) には出てこない、すなわち λX.(λn.F) = make-wa2 が式 (make-wa3Y make-wa3Y) の中で定義されていないから、ということでした。なので
λn.[n + {(λX.(λn.G) λX.(λn.G)}(n-1) ※〜] =λn.[n + (make-wa2 make-wa2)(n-1) ※〜]
が成り立たない、すなわち
λn.[n + {(λX.(λn.G) λX.(λn.G)}(n-1) ※〜] ≠λn.[n + (make-wa2 make-wa2)(n-1) ※〜]
なので (λX.(λn.G) λX.(λn.G)) がずっと計算され続けるからなのではないか、と推測しました。逆に make-wa2 を定義し名前を付け
(make-wa2 make-wa2)
とするとおそらく引数を与えられるまで展開(β簡約)されず一旦ここで止まり、例えば引数 a を与えられると
= ((make-wa2 make-wa2) a) =λn.[n + (make-wa2 make-wa2)(n-1) ※〜]a = [a + (make-wa2 make-wa2)(a-1) ※〜] = [a + λn.[n + (make-wa2 make-wa2)(n-1) ※〜](a-1) = [a + (a-1)λn.[n + (make-wa2 make-wa2)(n-1) ※〜]((a-1)-1) = ... = [a + (a-1) + (a-2) +...(make-wa2 make-wa2)0] = [a + (a-1) + (a-2) + ...0]
となって総和が求まるのではないかと考えました。つまり make-wa2 は名前を与えられている(定義されている)ので (make-wa2 make-wa2) は引数を与えられるまでは、その場で
(make-wa2 (λX.(λn.G)) = (λn.[n + {(λX.(λn.G) λX.(λn.G)}(n-1) ※〜] = (λn.[n + (λn.[n + {(λX.(λn.G) λX.(λn.G)}(n-1) ※〜](n-1) ※〜] = ...
と展開されないと考えたのです。
これを確かめるために
(make-wa3Y make-wa2) を評価してみると止まり ((make-wa3Y make-wa2) 10) = 55 に無事なりました。 これはおそらく
(make-wa3Y make-wa2) = λX.(λwa.λn.F (X X)) make-wa2 = λwa.λn.F (make-wa2 make-wa2) = λwa.λn.[n + wa(n-1) ※ただしn<=0ならば0] (make-wa2 make-wa2) = λn.[n + (make-wa2 make-wa2)(n-1) ※ただしn<=0ならば0]
となり引数が与えられるまで (make-wa2 make-wa2) が展開されないからだろうと考えました。 逆に (make-wa2 make-wa3Y) としますとこれは暴走しませんが、0 以外の正の数を引数を与えると暴走します。
((make-wa2 make-wa3Y) a) (ただし a>0) = ((λX.λn.G) make-wa3Y) a) = ((λX.λn.[n + (x x)(n-1) ※〜]) make-wa3Y) a) = ((λX.λn.[n + (make-wa3Y make-wa3Y)(n-1) ※〜]) a) = ((λX.λn.[n + {(λX.λn.G) (λX.λn.G)}(n-1) ※〜]) a) = ((λX.λn.[n + (make-wa2 make-wa2)(n-1) ※〜]) a)
となりそうですがSchemeは make-wa2 -> λX.λn.G は知っているけれど λX.λn.G -> make-wa2 は知らないから
= ((λX.λn.[n + {(λX.λn.G) (λX.λn.G)}(n-1) ※〜]) a) ≠ ((λX.λn.[n + (make-wa2 make-wa2)(n-1) ※〜]) a)
なのではないか、と推測しました。
くだくだと書いてきましたことを自分なりに要約しますと
・(make-wa2 make-wa2) が暴走しないのは引数が適用されるまで、名前を付けられた(定義された)関数は展開されないのではないか? それならば (make-wa3Y make-wa2) が (make-wa2 make-wa2) と同じく計算されることが説明できる。 ・名前を付けられていない式(例えば make-wa3Y = λX.{(λwa.λn.G)(X X)} であって、≠ λX.λn.G なので make-wa3Y ≠ λX.λn.G)は、それが引数になると裸のλ式として計算されてしまう。 ・名前を付けられている関数も、展開されるとその式が自分のことだと分からない。
だと推測しているということです。 これを考慮してWikipedia「評価戦略」を読み返してみると、「正格評価」の「値呼び」というのは引数が与えられるまで関数を計算しないことなのでは? また「作用的順序」では関数を作用させる以前に可能な限り関数を簡約することらしいが、関数に名前を付けていると(例えば (make-wa2 make-wa2) の場合)、そこで簡約が一旦止まるということではないか? と愚考しております。これらは妥当でしょうか?
……と考えましたが
make-wa3Y は純Yコンビネータの種で make-wa2 はZコンビネータの種で
(define YZ (lambda (f) ((lambda (X) (f (X X))) (lambda (X) (f (lambda (x) ((X X) x)))))))
とすると
(define wa (YZ (lambda (seed) (lambda (n) (if (<= n 0) 0 (+ n (seed (- n 1))))))))
では普通に (wa 10) = 55 になるので単にそういうことなのかとも思っています。 YZ = λf.(λx.f(xx))(λx.f(λy.xxy)) = \f.f((\x.f(\y.xxy))(\x.f(\y.xxy))) = Z だもんなあ……
1.
ある数に1を足す関数をy(y(a)=a+1)、ある数を2乗する関数をx(x(a)=x*x)とします。 これを組み合わせ、ある数に1を足して2乗する関数+1^2を作るとするとラムダでは
+1^2=(λx.x*x)(λy.y+1)
になると思います。
最初にyへ数を渡すので
+1^2(y)=λy.(λx.x*x)(λy.y+1)
で良いような気がしますがSchemeで
(define +1^2 (lambda (y) ((lambda (x) (* x x)) (lambda (y) (+ y 1)))))
と書いて引数を与えると
gosh> (+1^2 2) *** ERROR: operation * is not defined between #<closure ((|+1^2| |+1^2|) y)> and #<closure ((|+1^2| |+1^2|) y)>
と返ってきます。
「何故にまず +1^2 に +1^2 が適用されてるんだ?」とよく分かりませんでしたが
(define +1^2 (lambda (y) ((lambda (x) (* x x)) (+ y 1))))
では
gosh> (|+1^| 2) 9
と (2+1)*(2+1)=9 が返ってきました。これは最初のラムダ計算式が間違っていて
+1^2(y)=λy.(λx.x*x)(y+1)
こうなのでしょうか?
しかしこれならば
(define +1^2 (lambda (x) ((lambda (x) (* x x)) (+ x 1))))
でもよいわけで、こうだとラムダ計算式では
+1^2=λ.x(λ.x x*x)(x+1)
となりλ.xの連続を一体どう考えるといいのか分かりません。
2.
Zコンビネータ=λf.(λx.f(λy.(xx)y)) (λx.f(λy.(xx)y))
にfを適用して変換していくと(fにfを代入するので最初のλf.が省略されるだけですが)
(Z f)
=(λf.(λx.f(λy.(xx)y)) (λx.f(λy.(xx)y))f)
=((λx.f(λy.(xx)y)) (λx.f(λy.(xx)y)))
=f(λy.(λx.f(λy.(xx)y)) (λx.f(λy.(xx)y))y)
=f(λy.(λf.(λx.f(λy.(xx)y)) (λx.f(λy.(xx)y)))f)y)
=...
となるわけですが
(λx.f(λy.(xx)y))
が繰り返し出現するのでこの部分を定義してZを作られるのではと考え
(define Z- (lambda (x) (f (lambda (y) ((x x) y))))) (define Z (lambda (f) (Z- Z-)))
と試してみると
gosh> (Z 'g) *** ERROR: unbound variable: f
と返ってきました。
Z-で使っているfとZで使っているfは別の範囲で定義されているのでこうなるのは分かりますが
(define Z (lambda (f) (define Z- (lambda (x) (f (lambda (y) ((x x) y))))) (Z- Z-)))
と内部で定義する以外に同じ名前の変数を渡す(?)方法はありませんでしょうか?
Scheme:lambdaだけで再帰 を拝読し
gosh> ((make-wa2 make-wa2) 10) 55
と返ってきた瞬間ポカンして以来、折に触れてYコンビネータのことを考えている者です。
あまりに訳が分からなくて、ラムダ計算の方からも分からないなりに探っております。
Siro氏にご教授頂いたコードを自分にも分かるよう分解し、一旦f(x)式に書き直してハードルを下げ理解を進めようとする一方、Yコンビネータの理論からも何とか理解を進め、山の両側からトンネルを掘って繋がることを期待するという方針です。(繋がるといいのですが)
WikipediaのYコンビネータの項
も読んでみましたが
Yg = (λf . (λx . f (x x)) (λx . f (x x))) g = (λx . g (x x)) (λx . g (x x)) = (λy . g (y y)) (λx . g (x x)) = g ((λx . g (x x)) (λx . g (x x))) = g (Y g)
をf(x)で書いてみると
Y(f) = {f(x(x))}(f(x(x))) (左の式のxに(f(x(x)))を代入すると) = f{f(x(x))}(f(x(x))) ({f(x(x))}(f(x(x)))はY(f)なので) = f(Y(f)) ∴ Y(f) = f(Y(f))
という理解で合っていますでしょうか。
ただしこれだと
Y(f) = f(Y(f)) = f(f(Y(f))) = f(f(f(Y(f))))...
となって止まらないのではないかと思いました。実際
(define Y (lambda (f) ((lambda (x) (f (x x))) (lambda (x) (f (x x)))))) (Y 'g)
で C+x C+e しますと何も返ってこず、やがてPCのファンが唸り始め操作を受け付けなくなりました。そうです、暴走です。
上述のWikipediaの続きを読むと、どうやら
Zコンビネータ
というものにしなくては止まらないらしいとのこと。
そこでSiro氏にお教え頂いたコードY
(define Y (lambda (f) ((lambda (X) (f (lambda (x) ((X X) x)))) (lambda (X) (f (lambda (x) ((X X) x)))))))
をラムダ計算式にしてみると
Y = λf.(λX.(f(λx.XXx))(f(λx.XXx))
となり、Xをx、xをyに置き換えるとZコンビネータの式と同じになります。(多分)
ということはこれは正確にはYコンビネータなのではなく、Zコンビネータなのでしょうか。
「Yコンビネータを知らん奴にη変換の(η変換が何なのかいまだ知りませんが)Zコンビネータを教えたって分かるわけない」というのは重々承知しておりますが、トンネル掘りの方向の微修正が必要になると思いますので、Zコンビネータという理解で合っているか否か、ご存知の方がいらっしゃいましたらお教えください。
(x x)
の評価が先に行われてそこで無限ループしてしまいます。
ただ、Scheme含む多くの正格評価言語において、評価を遅らせるために式をlambdaで包むことは
よく行われるので、ラムダ算法の話をしてるのでなければZコンビネータの形式をYコンビネータ
(あるいは正格評価版Yコンビネータ)と言っちゃったりします。
(lambda (x) (f x))
を f
に置き換える変換です。
(ここでf
はx
を含まない、関数に評価される式)。callback = function (x) { myfunc(x) }
と書いても callback = myfunc
と書いても同じ、ということです。f
がいつ評価されるかに差が出ます。
以前、hamayama氏に https://practical-scheme.net/wiliki/wiliki.cgi?Scheme%3A%E5%88%9D%E5%BF%83%E8%80%85%E3%81%AE%E8%B3%AA%E5%95%8F%E7%AE%B1#H-aissbn に対して
(define (make-wa3 X) (define (F wa) (define (G n) (if (>= 0 n) 0 (+ n (wa (- n 1))))) G) (define (H a) ((X X) a)) (F H))
と書き直していただきました。 自分にはまだちょっと分からなかったので
(define (G n) (if (>= 0 n) 0 (+ n (wa (- n 1))))) (define (F wa) G) (define (H a) ((X X) a)) (define (make-wa3 X) (F H)) (define wa (make-wa3 make-wa3))
と各関数に分解してみました。順に評価したところ
gosh> (wa 10) 55
と、無事に出来ました。 これらをf(x)方式で書き直して
G(n)=(略) #1
F(wa)=G #2
H(a)={X(X)}(a) #3
make-wa3(X)=F(H) #4
wa=make-wa3(make-wa3) #5
と書き直すと
#3より
H=X(X) #3'
#2と#3'と#4より
make-wa3(X)=F(H)=F{X(X)}
よって#5と#2より
wa=make-wa3(make-wa3)=F{make-wa(make-wa3)}=F(wa)=G
で理解は合ってますでしょうか。 確かに5つの式を評価したあと
(G 10)
で
gosh> (G 10) 55
と返ってきましたが、正しい理解なのか分かりません。どこか誤解がありませんでしょうか。
(define (G n wa) (if (>= 0 n) 0 (+ n (wa (- n 1))))) (define (F wa) (cut G <> wa)) (define (H a X) ((X X) a)) (define (make-wa3 X) (F (cut H <> X))) (define wa (make-wa3 make-wa3)) (wa 10) ;; ==> 55 (define wa (make-wa3 0)) (wa 10) ;; ==> ERROR
(define (G n) (if (>= 0 n) 0 (+ n (G (- n 1))))) (define wa G) (wa 10) ;; ==> 55
--
(define wa0 #f) (define X0 #f) (define (G n) (if (>= 0 n) 0 (+ n (wa0 (- n 1))))) (define (F wa1) (set! wa0 wa1) G) (define (H a) ((X0 X0) a)) (define (make-wa3 X1) (set! X0 X1) (F H)) (define wa (make-wa3 make-wa3)) (wa 10) ;; ==> 55
しつこく申し訳ありません。前の質問へのご回答(有難うございました)を自分なりに解釈してみたところ、自分が理解していないのは、詰めてみますと、以下のようです。
(lambda () 1) ;gosh> #<closure #f> ((lambda () 1)) ;gosh> 1
1)前者が#<closure #f>(クロージャの偽)になるのはなぜか。
2)括弧がもう一重深い後者が数値になるのはなぜか。
愚問ですが、ここを理解したく存じております。
(構文キーワード フォーム ...)
(式1 式2 ...)
if
や lambda
など) が先頭に来る場合で、
構文キーワードによって特有の意味を持ちます。これはそれぞれ覚えるしかありません。
(マクロを使ってユーザが拡張することもできますが、ひとまず置いておきます)。
式1(式2, ...)
と書かれるのと同じです。先頭が構文キーワードでなければこちらの形式とみなされます。
(式)
と書いてあったらそれは無引数の関数呼び出し、
よくある言語では 式()
と同じ、ということです。
(lambda () 1)
=> function () {return 1}
((lambda () 1))
=> (function () {return 1})()
おそらく分かる方には当たり前なことだと思いますが、現状私一人ではよく分かりません。
(define san () (+ 1 2)) ;gosh> *** ERROR: Compile Error: syntax-error
なのに
(define (san) () (+ 1 2)) ;gosh> san ;gosh> san ;#<closure san> ;gosh> (san) ;3
なのは何故でしょうか? ちなみに
(define san (+ 1 2)) ;gosh> san ;gosh> san ;3
これはこうなりました。
Shiro(2019/12/24 23:45:46 UTC): defineの基本形は
(define 変数 式)
です。最初の例は式の部分が()
なのにさらに余分な(+ 1 2)
がついてるのでエラーになります。
ところでdefineにはもうひとつの形式があって:
(define (変数 引数 ...) 式 ...)
これは次の形式の略記です。
(define 変数 (lambda (引数 ...) 式 ...))
二番目の例はこちらにあたります。略記せずに書くと:
(define san (lambda () ; これは無引数であることを示す() () ; これは空リストになる式だが、次にまた式があるので結果は捨てられる (+ 1 2)))
というわけです。
ソースからのmake+installで躓いています。お力添えをいただきたく。 tarballを展開後、./configure、makeを実行すると以下のメッセージでabortしてしまいます。
TARGETLIB="`pwd`" gcc -std=gnu99 -g -O2 -Wall -Wextra -Wno-unused-label -fPIC -DPIC -Wl,--rpath="`pwd`" -L. -rdynamic -o gosh main.o -lgauche-0.97 -lutil -lm -lpthread ld: error: undefined symbol: GC_get_total_bytes >>> referenced by main.c:420 >>> main.o:(cleanup_main) ld: error: undefined symbol: GC_get_heap_size >>> referenced by main.c:420 >>> main.o:(cleanup_main) ld: error: undefined symbol: GC_init >>> referenced by main.c:685 >>> main.o:(main) collect2: ld returned 1 exit status *** Error 1 in src (Makefile:255 'gosh') *** Error 1 in /usr/src/Gauche-0.9.8 (Makefile:43 'all')
実行環境は macOS High Sierra 10.13.6 の VMware fusion 上で作成した OpenBSD 6.5 です。 ./configure 時に以下のメッセージが表示されていたのが気になっています。warning とその前後数行です。
checking if g++ supports -c -o file.o... (cached) yes checking whether the g++ linker (/usr/bin/ld) supports shared libraries... yes checking dynamic linker characteristics... openbsd6.5 ld.so checking how to hardcode library paths into programs... immediate checking for inline... inline configure: WARNING: "Explicit GC_INIT() calls may be required." checking for pthread_self in -lpthread... yes checking whether compiler supports -Wextra... yes checking whether compiler supports -Wpedantic... no checking for xlc... no checking whether compiler supports -fno-strict-aliasing... yes
よろしくお願いいたします。
Scheme の言語仕様では、既にアロケート済みのストレージを改変するような手続きのことを mutation procedure という用語で表しています。
しかし、 SRFI-123 などでは modifier という語も使われています。 modifier とmutation procedure の間に区別はあるのでしょうか?
雰囲気としては、 modifier は accessor に対応づいているもの (Gauche の用語でいう setter に近い?) の印象で捉えているのですが、実際のところはどんなものでしょう?
ポートに対してなにか処理を行うreaderやwriterを実装する際のスタイルに関する質問です。
readerやwriterを実装する際、call-with-fooとwith-fooのどちらにも対応させるためにオプショナル引数portをとる手続きとし、デフォルトではこれを現在のポートに束縛、そして内部の処理をwith-input-from-port/with-output-to-portでくるむことで、より低レベルなリーダーを引数なしで使えて便利なことに最近気がつきました。恣意的な例ではありますが、以下のようなイメージですね。
(define (read-2-chars :optional (port (current-input-port))) (with-input-from-port port (^[] (values (read-char) (read-char)))))
そこで質問なのですが…。 上記のように、処理の本体(ここでは(values ...))をサンクにするために(lambda () ...)でくるむのが、なんだか不恰好なように感じてしまうのです(もちろん、サンクにしなければならない理由はわかるのですが…)。中身が単純な手続きならばcutでどうにでもなりますが、たとえばmatchを使いたいとなるとそうもいきません(いかない、はず…)。
自分の場合、つい以下のように内部defineでサンクを別に定義してしまいたくなるのですが、あまり他ではこのようなスタイルを見かけないように思います(といっても初心者ですのであまり多くの例を見たことがあるわけではないのですが…)。
(define (read-2-chars :optional (port (current-input-port))) (define (thunk) (values (read-char) (read-char))) (with-input-from-port port thunk))
こういう際に推奨されるスタイルや、上記のようなスタイルのデメリットなどはあるのでしょうか? よろしくお願いいたします。
{...}
を (^[] ...)
に読み替えるリーダマクロを入れようとしたこともあるんですが、そのためにカーリーブレースを使ってしまうのもなあ、と思い止まりました。#(...)
でもいいのかな…Clojureの#(...)
とは微妙に異なるのが混乱の元になりそうな気もするが…
(tell-port port)
みたいなport引数が必須の手続きを使ったりすると見た目がややこしいのでそういう場合は使わないくらい)、後者についてはnamed letでloop
という名前を付けるのと似た感じで、狭いスコープ内でならthunk
でいいのかなというところで、しばらく使ってみようかなと思いました。test
に対するtest*
のようにwith-input-from-port*
とかも考えてみたのですが、Racketとかだと抜けたときにcloseするかどうかの区別に使っているっぽいので微妙かな…とも思ってやめた経緯があったりします。#(...)
のような構文はあるといいなと思うことがときどきありますね。cut
やpa$
ではcarの部分にメインの処理(として考えたい手続き)が来ないという見た目の問題もあったりするので…。#(...)
はネストできないというのがSchemer的には
結構厳しい制限と感じるんですよね。
いろいろいじっててネストが出来た時にどちらかfn
で書き直さないとならないのが思考を止める感じになるんで。
遅延シーケンスについて調べています。 以下のコードで add-10 の呼ばれるタイミングについて質問なのですが
(use gauche.lazy) (define (add-10 n) (print "call add-10 n: " n) (+ n 10)) (define (f n) (ltake (lmap add-10 '(1 2 3)) n))
gosh> (f 0) call add-10 n: 1 () gosh> (f 1) call add-10 n: 1 call add-10 n: 2 (11) gosh> (f 2) call add-10 n: 1 call add-10 n: 2 (11 12) gosh> (f 3) call add-10 n: 1 call add-10 n: 2 (11 12 13)
(f 3) を呼び出したときに call add-10 n: 3 のログが出力されないのは何故でしょうか? (このログが出ていないということは (add-10 3) が呼ばれていないと認識しているのですが……。 しかし戻り値 (11 12 13) をみると (add-10 3) は確かに呼ばれているようです。これが分からない)
環境: Gauche scheme shell, version 0.9.6 [utf-8,pthreads], x86_64-apple-darwin15.6.0
たとえば
(define add+1 (lambda (a) (+ a 1)))
と
(define (add+1 a) (+ a 1))
は"同値"でしょうか?(同じ引数に対して返ってくる値が同じという意味ではなくて、関数の意味・定義そのものが同じという意味において)
(define make-wa3 (lambda (X) ((lambda (wa) (lambda (n) (if (>= 0 n) 0 (+ n (wa (- n 1)))))) (lambda (a) ((X X) a)))))
と(個人的に大文字のXと小文字とxを混同しそうなのでxをaに変えてあります)
(define (make-wa3 X) (define (wa n) (if (>= 0 n) 0 (+ n (wa (- n 1))))) (define (X a) (wa ((X X) a))))
は"同値"でしょうか。よろしくお願いします。
(define (make-wa3 X) (define (F wa) (define (G n) (if (>= 0 n) 0 (+ n (wa (- n 1))))) G) (define (H a) ((X X) a)) (F H))
マクロ定義の <literals> に現れる識別子が束縛されている場合で、マクロを呼び出す時には束縛が見えない場合、リテラルのパターンにマッチするものでしょうか?
(let-syntax ((test (syntax-rules () ((_ arg) (let ((yelse '(value))) (let-syntax ((test-aux (syntax-rules (yelse) ((_ yelse) 'ok) ((_ x) 'no)))) (test-aux arg))))))) (test yelse)) ;; ここでは `yelse` は束縛されていない ;;=> ok ;; 正しい?
'no
が返って来るのではないかな?と考えたのですがそうでない理由がわかりませんでした。
以下に質問の経緯を書きます。
R5RSのパターン言語の所の<リテラル>の扱いがよくわからない部分があったので質問します。<リテラル>の扱いについてこんな記述があります。
A subform in the input matches a literal identifier if and only if it is an identifier and either both its occurrence in the macro expression and its occurrence in the macro definition have the same lexical binding, or the two identifiers are equal and both have no lexical binding.
後者(or〜以降)は、二つの識別子が同じで、両方とも字句的束縛がされていない、ということは、例えば、 yelse
の束縛が無い場合に以下のような結果になる、ということだと考えられます。
(let-syntax ((test (syntax-rules (yelse) ((_ yelse) 'ok) ((_ x) 'no)))) (test yelse)) ;;=> ok
前者がどんな例があるかを考えます。素直に考えると以下のものが思い付くのだけれども、上の例との違いがほとんど無いので、いまいちはっきりとしません。これをもってして、“扱いがよくわからない”、と考えています。
(the macro expression って?というのもあるのですが、以下の場合では、多分、 (test yelse)
の式のことだと思うのですが自信無いです…)
(let ((yelse '(value))) (let-syntax ((test (syntax-rules (yelse) ((_ yelse) 'ok) ((_ x) 'no)))) (test yelse))) ;;=> ok
自分なりに考えを整理すると、見た目・字面が同じなので、単に字面で <literals> を比較しているのでは?という疑問が残る、ということです。
この疑問を解消するには、(1)それぞれ異なる場所に束縛されている識別子を比べてみる、(2)どちらか一方のみが、ある場所に束縛されている識別子を比べてみる、その結果、リテラルのパターンにマッチしないということを確認すれば納得できると考えたので、やってみました。
「それぞれ異なる場所に束縛されている識別子を比べてみる」もの: 'no
が返ってくると考えます。
(let ((yelse '(value))) (let-syntax ((test (syntax-rules (yelse) ((_ yelse) 'ok) ((_ x) 'no)))) (let ((yelse #f)) (test yelse)))) ;;=> no ;; 正しい結果だと考えられます
「どちらか一方のみが、ある場所に束縛されている識別子を比べてみる」もの: まず、マクロを呼び出している所では、ある場所に束縛されている場合。やっぱり 'no
が返ってくると考えます。
(let-syntax ((test (syntax-rules (yelse) ((_ yelse) 'ok) ((_ x) 'no)))) (let ((yelse '(value))) (test yelse))) ;;=> no ;; 正しい結果だと考えられます
一方、マクロ定義で <literals> に現れる識別子が、ある場所に束縛されている場合、というのが簡単には書き下ろしずらいです。以下のような、別のモジュールから呼び出すという方法を思い付きました。(他に何か方法があるのだろうか?思い付いたのが質問の冒頭に記述したものです。)
(select-module user) (define-module t.syntax-rules-test (export-all)) (select-module t.syntax-rules-test) (define yelse '(value)) (define-syntax test-literal-bound (syntax-rules (yelse) ((_ yelse) 'ok) ((_ x) 'no))) (test-literal-bound yelse) ;;=> ok ;; 正しい結果 ;; これだけだと字面で比較しているかもしれないという疑問が残ります (select-module user) (import (t.syntax-rules-test :prefix t:)) (t:test-literal-bound t:yelse) ;;=> ok ;; 正しい結果のようにも思いますし、実装依存のような気もします。 ;; 同じ場所に束縛されている識別子なので 'ok が返る(字面が違うのに注目します) ;; ただし、インポートを上記のように使うとこの結果が*たまたま* 'ok が返るよう ;; に識別子がインポートされる、ということの裏返し、とも考えられます。 ;; つまり、これは実装に依存する動作である、とも考えられます。 (let ((yelse t:yelse)) (t:test-literal-bound yelse)) ;;=> no ;; 正しい結果 ;; 異なる場所に束縛されている識別子なので 'no が返る(字面は同じ) ;; XXX: これがわからない (t:test-literal-bound yelse) ;;=> ok ;; 正しい? ;; マクロ呼び出しの `yelse` はどの場所にも束縛されていない。 一方で、 ;; マクロ定義の `yelse` は束縛されている。なので 'no だと考えられるけれども…
最後の、 (t:test-literal-bound yelse)
では 'no
が返って来るのではないかな?と考えたのですがそうでない理由がわかりませんでした。
よろしくお願いします。
例えば 1から n までの自然数の和(wa)から n を引いた数を返す関数(wa-n)を考えます。 まず1から n までの自然数の和は
(define (wa n) (if (>= 0 n) 0 (+ n (wa (- n 1)))))
なので wa-n は
(define (wa-n n) (if (>= 0 n) 0 (- (wa n) n)))
で、4 を渡すと (4+3+2+1)-4=6 が返ってきます。(そんなんwaに(n-1)を渡したら返ってくる数と同じじゃん、というのはとりあえず) これをラムダを使って一つの関数にしようとすると
(define (wa-n2) (if (>= 0 n) 0 (- (lambda (i) (if (>= 0 i) 0 (+ i (.....(- n 1)))));※ n) n)))
というようになると思いますが、※の行の (.....) が思い付きません。この様な場合、局所関数を定義するか、letrec を使うしか(或いは名前付きletとかdoを使うとか)方法は無いのでしょうか? ラムダの中だけで再帰は不可能なのでしょうか?
(map (lambda (ls) (cons 'a ls)) '(a b c))
だと
((a . a) (a . b) (a . c))
が返ってきましたので、
(map (lambda (ls) (cons 'a ls)) '())
を評価すると
(a)
が返ってくると思っていたのですが
()
が返って来ました。今ひとつ理解できません。map の第2引数にヌルを渡すと、必ずヌルが返ってくるのでしょうか。
(map (lambda (ls) (cons 'a ls)) '(a b ())) ((a . a) (a . b) (a))
になるのですが。 また何となく
(map (cons 'a) '(a b c))
で行ける気がしたのですが 「cons は2つ引数が要るんじゃ!1つしか貰っとらん!」 と怒られました。 map の第一引数に2つ(以上)の引数が必要な関数を使う場合、lambda で定義しないといけないのでしょうか?
(map cons '(1 2 3) '(a b c))
では
((1 . a) (2 . b) (3 . c))
と仕事してくれるのですが、どう理解したものか分かりません。
()
と (())
を区別することが肝要かと。
()
は空リスト、つまり要素を持たないリスト
(())
は要素をひとつ持つリストで、その要素とは()
。
()
なら、要素がないので適用するものがありません。結果も要素のないリスト()
となります。
(())
の場合は、listは1要素のリストなのでその要素にfが適用されます。その要素とは()
です。mapの結果も1要素のリストになります。
gosh> (map (lambda (ls) (cons 'a ls)) '()) () gosh> (map (lambda (ls) (cons 'a ls)) '(())) ((a))
GHCi, version 7.10.3: http://www.haskell.org/ghc/ :? for help Prelude> let kons x y = x:y Prelude> kons 1 [2] [1,2] Prelude> kons 1 <interactive>:4:1: No instance for (Show ([a0] -> [a0])) (maybe you haven't applied enough arguments to a function?) arising from a use of `print' In the first argument of `print', namely `it' In a stmt of an interactive GHCi command: print it Prelude> :t kons 1 kons 1 :: Num a => [a] -> [a] Prelude> :t kons 1 [2] kons 1 [2] :: Num a => [a]
n個の数要素を持つリストを、car部分を消去しながらn個のリストにするコード、nlistとn2listを書いてみました。
(define (nlist ls) (if (null? ls) '() (cons (cons (car ls) (cdr ls)) (nlist (cdr ls)))))
と
(define (n2list ls) (if (null? ls) '() (cons ls (n2list (cdr ls)))))
です。この二つは同じだと思うのですが、
gosh> (nlist '(1 2)) ((1 2) (2))
gosh> (n2list '(1 2)) ((1 . #0=(2)) #0#)
と、後者では繰り返し(?)を表す(のだと思う) "#" タグで 2 が返ってきます。何故でしょうか。
また、マクロで返ってくるコードを実行する場合、このように "#" タグが使われていても、問題なく処理されるのでしょうか。
よろしくお願いします。
#n=
は、リストの部分共有があることを示しています。図で書くとこんな感じで、(2)
のセルが両方からポイントされてるわけですね。
+---+---+ +---+---+ | * | *---->| * | ()| +-|-+---+ +-|-+---+ | | | \---\ | | | +---+---+ \-->+---+---+ \--->| 1 | *------->| 2 | ()| +---+---+ +---+---+部分共有が無い場合はこうなります。
+---+---+ +---+---+ | * | *---->| * | ()| +-|-+---+ +-|-+---+ | | +---+---+ | \------>| 2 | ()| | +---+---+ | | +---+---+ +---+---+ \--->| 1 | *------->| 2 | ()| +---+---+ +---+---+
(2)
のリストを破壊的変更しなければ、ふたつの差はほとんどありません(後者の方がメモリを使う、というくらいです。) マクロ展開後のコードを破壊的変更することは普通ありませんから、マクロ出力に#n=
ラベルが含まれていても問題ありません。((1 2) (2))
と出力しても構わないことになっています。実際、混乱が多いので、開発版(0.9.6になるもの)では既にREPLではデフォルトで部分共有は明示しないようになりました。#0=
表記が見にくかったら、結果をwrite
に渡せばラベルなしの出力が得られます。
質問者です。お答えありがとうございました。 「nlist は再帰で (nlist '(2)) になった場合 (cons (car '(2)) (cdr '(2))) で新たに (2) というリストが作られるが、n2list の場合 (cons ls (n2list (cdr ls))) で (1 2) の cdr 部分 (2) が共有される、『使い回される』(?)ので同じポインタで指し示される」と理解して合っていますでしょうか?
R5RS や R7RS にはこのような文言があります。
定義によって導入されていないグローバルの束縛を変更することは, 本章で定義された手続きの振舞いに対して未規定の効果をもつ。
「定義によって導入されていない」というのはどのような意味でしょうか?
R5RS については、この言葉は「プログラマが define を書いていない (要するに処理系が提供する束縛)」 くらいの意味だと思っていました。
しかし、R7RS だと「import した変数へ set! した効果はエラー」という旨の記述があり、 デフォルト環境は空っぽなので処理系が提供する全ての変数は import を経なければ使えません。 処理系が提供する変数については import した変数についての規定で説明がついてしまい、 「定義によって導入されていない」という文言が有る意味がわからなくなってしまいました。
S式をトラバースして特定のシンボルだけを置換したいと思い、以下のようなコードを書いたのですが、これと似た動作をする関数がGauche内にあれば教えて頂きたいです。上手く探しきれずにすみません。
(define (map-tree proc sexp) (if (pair? sexp) (cons (map-tree proc (car sexp)) (map-tree proc (cdr sexp))) (proc sexp)))
tree-walk TREE PROC LEAF? WALKERこれはTREEの構造を特定せず、葉の判別述語LEAF?とノードの子供を訪れるWALKERを外から与えるものです。これを使うとmap-treeは次のとおり書けます。
(define (map-tree proc sexp) (tree-walk sexp proc (complement pair?) (^[f p] (cons (f (car p)) (f (cdr p))))))ただ、このくらいならmap-treeを直接書いてしまう方が分かりやすいですね。 map-treeもtext.treeに入れようかな。
Gaucheの拡張を書いている時にgauche.configureを使用してconfigureスクリプトを書いたのですが、その時に気になる事があったので質問します。
configureスクリプトを書いている時に以下の問題がおきました。
・問題
(cf-check-headers)でヘッダをチェックしてconfig.h.inからconfig.hを生成しようとしたが、ヘッダが見つかったにも関わらず#define HAVE_XXX_Hが出力されませんでした。
・調査
gauche/configure.scmを調べた所、#define HAVE_XXX_Hを出力する箇所(748行目)で比較が常に失敗しているために#define ...が絶対に出力されない事が分かりました。
・修正
ヘッダが見つかった際には比較が成功するように修正した所、正しくconfig.h.inから#define HAVE_XXX_Hが出力されたconfig.hが生成されました。
私の環境では上記の修正で望む動作が実現してその他のconfigureの動作も問題ないのですが、設計思想の面などからこの修正は良かったと言えるのでしょうか。
以下は環境と修正のパッチです。
OS: Ubuntu 14.04 LTS
Gauche: 0.9.5
configure: ./configure '--prefix=/usr/local/gauche-0.9.5' '--enable-threads=default' '--with-tls=axTLS' '--with-dbm=dbm,odbm' '--enable-multibyte=utf-8' 'PKG_CONFIG_PATH=/usr/local/pkgconfig'
patch:
--- ../Gauche-0.9.5/lib/gauche/configure.scm 2016-08-06 19:47:50.000000000 +0900 +++ /usr/local/gauche/share/gauche-0.9/0.9.5/lib/gauche/configure.scm 2017-07-14 08:49:38.699387698 +0900 @@ -111,7 +111,7 @@ (tarname :init-keyword :tarname) (tool-prefix :init-form #f) ; cross compilation tool prefix (config.h :init-form '()) ; list of (config-header . source) - (defs :init-form (make-hash-table 'eq?)) ;cf-define'd thingy + (defs :init-form (make-hash-table 'string=?)) ;cf-define'd thingy (substs :init-form (make-hash-table 'eq?)) ;cf-subst'ed thingy (precious :init-form (set eq-comparator)) ;vars overridable by env (features :init-form (make-hash-table 'eq?)) ;enabled features by cmdarg @@ -745,7 +745,7 @@ (^[line outp] (rxmatch-case line [#/^#undef\s+([A-Za-z_]+)/ (_ name) - (if-let1 defval (dict-get (~ pa'defs) (string->symbol name) #f) + (if-let1 defval (dict-get (~ pa'defs) name #f) (display #"#define ~name ~defval" outp) (display #"/* #undef ~name */" outp))] [else (display line outp)])
REPLで組み込みの手続きはsubrと表示され、自分で定義したものはclosureと表示されます。そもそもSchemeでは手続き(procedure)という用語を使うようですが、どうしてこうなっているのでしょうか?
gosh> cons #<subr cons> gosh> (define x (lambda () 123)) x gosh> x #<closure x>
なお http://chaton.practical-scheme.net/gauche/a/2010/09/10 では
Gaucheのクラス名だと,最初から入ってる関数がsubr,作った関数がprocedure,最初から入ってる文法がsyntax,作った文法がmacroになるっぽい.
とありますが、現在の0.9.4ではprocedureでなくclosureと表示されます。仕様が変わったのでしょうか?
source-code
でソース情報などが参照できる可能性があるが、subrの場合は参照できない (組み込みでプリコンパイルされてる手続きはclosureであってもソース情報が落とされてるのでソースが見られないけど)。closureはdisassembleできるけどsubrはできない。等。
lisp全般に言えることですが、ライブラリ固有のマクロや独自に定義したマクロをうまく自動整形する仕組みはないのでしょうか。ちまちま手作業でインデントしたり、lisp-indent-functionを設定したりするのがわずらわしいのですが、皆さまはどのように対応していますか。(put 'keyword 'lisp-indent-function num)は特定のファイル、ライブラリでしか使われない構文がそのlisp方言全体の設定として保存されるのが好ましくありません。理想的にはエディタがファイル内のimport文を読み取ってそのライブラリ特有のインデントのルールをそのファイルに適用してほしいです。
Gauche-mingw-0.9.4.msiで、日本語名のファイルを含むディレクトリに対して (diectory-list2 directory-path)をすると
Windowsで日本語名のファイルを取り扱うスクリプトを書くにはどうすると良い のでしょうか?
以前の環境では、cygwinでビルドしたgaucheでは問題なかったと記憶しているので、 それが無難でしょうか? (2016/05/22 13:11:03 UTC)
*** SYSTEM-ERROR: stat failed for C:\xxx\日本語の文字化け No such file or directoryソースの win-compat.h で、stat を、ワイド文字対応の _wstat の define にすればよいように思いますが、
NB: stat() needs special treatment; MinGW defines its own macro.エラーメッセージの日本語の文字化けについては、Gauche:Windowsコンソール関連 の msjis の項目に説明があります。
ポール・グラハム著のOn Lispに載っているcondletをsyntax-rulesで再現しようしたのですが,どうにもうまく行かなくなったので質問致します.まずひとつ目は私が書いたマクロの誤りを教えていただきたいです.
;; 定義 ;; ローカル変数を抽出する (define-syntax cond-let (syntax-rules () ((_ ((condition (var val) ...) ...) body ...) (cond-let% () ((var ...) ...) ((condition (var val) ...) ...) (body ...))))) ;; 抽出した変数のリストを平坦化する (define-syntax cond-let% (syntax-rules () ((_ (var% ...) () clauses bodies) (cond-let%% () (var% ...) clauses bodies)) ((_ (var% ...) ((var ...) rest ...) clauses bodies) (cond-let% (var% ... var ...) (rest ...) clauses bodies)))) (define-syntax cond-let%% (syntax-rules () ((_ vars%% () ((condition (var val) ...) ...) bodies) ;; 外側の骨格を作る (cond (condition (let ((var val) ...) (cond-let%V () vars%% (var ...) bodies))) ...)) ((_ (var%% ...) (var0% var1% ...) clauses bodies) (letrec-syntax ((cond-let%%% (syntax-rules (var%% ...) ;; 抽出した変数の中の重複するものを取り除く ((_ var%% vars%% vars%) (cond-let%% vars%% vars% clauses bodies)) ... ((_ var%%% vars%% vars%) (cond-let%% (var%%% . vars%%) vars% clauses bodies))))) (cond-let%%% var0% (var%% ...) (var1% ...)))))) (define-syntax cond-let%V (syntax-rules () ((_ (var%V ...) () vars (body ...)) ;; 未束縛の変数に'()を束縛する (let ((var%V '()) ...) body ...)) ((_ (var%V ...) (var0%% var1%% ...) (var ...) bodies) (letrec-syntax ((cond-letV (syntax-rules (var ...) ;; 未束縛の変数を抽出する ((_ var vars%V vars%% vars) (cond-let%V vars%V vars%% vars bodies)) ... ((_ varV vars%V vars%% vars) (cond-let%V (varV . vars%V) vars%% vars bodies))))) (cond-letV var0%% (var%V ...) (var1%% ...) (var ...)))))) ;; 想定している動作 (cond-let (((= 1 2) (x 'a) (y 'b)) ((= 1 1) (y 'c) (x 'd)) (else (x 'e) (z 'f))) (list x y z)) => (cond ((= 1 2) (let ((x 'a) (y 'b)) (let ((z '())) (list x y z)))) ((= 1 1) (let ((y 'c) (x 'd)) (let ((z '())) (list x y z)))) (else (let ((x 'e) (z 'f)) (let ((y '())) (list x y z))))) => (d c ())
実際は`unbound variable: x'とエラーが出ます.bodyにローカル変数を使わなければ正しくリストが返ります.オリジナルの版と比べてもやたらと長く,おそらく筋の悪い書き方なので申し訳ないのですがよろしくお願いします.
もうひとつはGaucheではどのようにマクロをデバックすれば良いかということです.macroexpandを用いてもlet(rec)-syntaxより先まで式が展開されず,想定通りの式が出来上がっているのか分からないのでそこで手詰まりになります.何か良い方法があったらアドバイスください.
よろしくお願いします
macroexpand-all
が使えます。
gosh> (macroexpand-all '(cond-let (((= 1 2) (x 'a) (y 'b)) ((= 1 1) (y 'c) (x 'd)) (else (x 'e) (z 'f))) (list x y z))) (if (= '1 '2) (letrec ((x.0 'a) (y.1 'b)) (letrec ((z.2 '())) (list x.0 y.1 z))) (if (= '1 '1) (letrec ((y.3 'c) (x.4 'd)) (letrec ((z.5 '())) (list x.4 y.3 z))) (letrec ((x.6 'e) (z.7 'f)) (letrec ((y.8 '())) (list x.6 y z.7)))))
x.0
等となっているのは、
衛生マクロの変数衝突回避のリネームがかかっているからです(内部的にはidentifierになってるんですが、
macroexpand-all
がわかりやすいようにシンボルに直しています。また、変数リネームにより束縛が解決された後ではlet
は全てletrec
として扱って構わないので、macroexpand-all
は全てletrec
にして返します)
z.2
などと元のフォームのz
が同一視されていないことがわかります。
最近のラノベのタイトルのような質問で申し訳ありませんが、分かりやいようにと思いこの様な質問に致しました。ご不快に思われたら申し訳ありません。
例えばリストの個数を数える関数、kosuを再帰で作って、traceしてみます。
(use slib) (require 'trace) (define (kosu x) (if (null? x) 0 (+ 1 (kosu (cdr x))) ) ) (trace kosu) (kosu '(a b c)) gosh> CALL kosu (a b c) CALL kosu (b c) CALL kosu (c) CALL kosu () RETN kosu 0 RETN kosu 1 RETN kosu 2 RETN kosu 3 3
はい、リストの数を数えてくれました。だがしかし、ここで私には上手く分からない疑問が2つあります。
其の1
(if (null? x) 0[若しくは '()] 再帰[大抵は元のリストをcdrする])
は、初心者向けのScheme本によく出てくる例ですが、ここで最初に疑問に当たりました。
何故ならばtraceして評価してみると
CALL kosu ()
とありますように、リストをcdrしていくと最後は () つまりnullになるわけですから
(if (null? x) 0
のところで引っ掛かり((null? x)が#tになり)、0が返って来て、そこで終了になるように思えるのです。 実際、
(if #t 0 1)
を評価しますと、必ず0が返ってきます。同じことが起きないのは何故なのでしょうか。
(kosu x) = (+ 1 (kosu (cdr x))))日本語に訳すと「x の kosu は (cdr x) の kosu に 1 を足したもの」となります。 ただし、ヌルリストの cdr は取れないので、ヌルリストの kosu は 0 と定義してしまいます。 ヌルリストには要素がありませんから妥当な定義と言えるでしょう。
(kosu '()) = 0最初の定義と合わせると
(kosu x) = (+ 1 (kosu (cdr x)))) ただし (kosu '()) = 0これを Scheme に翻訳すると
(define (kosu x) (if (null? x) 0 ; ヌルリストの kosu は 0 (+ 1 (kosu (cdr x))))) ; それ以外は cdr の kosu に 1 を加える
其の2
この関数、「kosu」は
(kosu '(a b c))→(+ 1 (kosu '(b c)))→(+ 1 (+ 1 (kosu '(c))))→(+ 1 (+ 1 (+ 1 (kosu '()))))→(+ 1 (+ 1 (+ 1 0)))
を計算して3を返しているのだと思いますが(違っていたらすみません)、私には
(+ 1 (kosu (cdr '(a b c))))→(+ 1 (kosu '(b c)))
↓
(+ 1 (kosu (cdr '(b c))))→(+ 1 (kosu '(c))
↓
(+ 1 (kosu (cdr '(c))))→(+ 1 (kosu '())
そして if の条件が真になり 0 が返ってきて(何故ここで0が返ってこないか疑問であるのは、其の1で述べた通りですが、ここでは無視します)
(+ 1 0)
となり、1が返ってくるように思えるのです。つまり、(+ 1 (+ 1 (+ 1 0))) の左側二つの"1"たちは、足されようとしたら足すはずの相手が再帰で振り出しに戻るので、「そうか、またあそこへ戻るのかい……じゃあ新しい1と上手くやんな。あばよ」と去っていくのではないかと思えます。
左側二つの"1"たちは、どこで(+ 1 0)の答えを待っているのでしょうか。関数自体が記憶しているわけではないと思いますが……優しさでしょうか。しかし"0"に二股どころか三股掛けられても平気なわけではないと思います。 これは一体どう解釈すれば良いのでしょうか。よろしければ詳しい方、ご教授お願いいたします。(杜仲茶)
かしこ
追記:試しに
(define (kosu x) (if (null? x) 0 (+ (kosu (cdr x)) 1) ) )
と、最後の式の順序を入れ替えましたところ、同じように '(a b c) に対して3が返って来ましたし、traceしても同じ結果でした。「そりゃ足し算は順序関係ないけどさー」と思いつつも、1を足す前に「振り出しに戻る」を繰り返すのですから、ますます
(+ 0 1)
だけが評価されて終わる気がするのです。
追記の追記:この順序だと
(kosu '(a b c)) (+ (kosu '(b c)) 1) (+ (+ (kosu '(c)) 1) 1) (+ (+ (+ (kosu '()) 1) 1) 1)
となり
(+ (+ (+ (+ 0) 1) 1) 1)
となると思うのですが、今回は右の "1" たちが残っている理由が分かりません。
上手く分からない理由を上手く説明できなくてすみません。
すみません。このページでの質問の仕方が分からないのですが、こうやって「編集」をして質問するのでしょうか?
hateman? 任意の引数が存在する場合の関数の宣言方法はどうすればいいのでしょうか。 例えば 文字列の足し算や
(+ "hoge" "fuga" "aaa") # -> "hogefugaaaa"
A, B, C は(独自の)行列クラスの場合の掛け算
(* A B C) # -> ABC
などは generics 型によって動的にどの手続きを呼ぶか判断されると思いますが、 これが任意の引数の場合に型で判断できるように +, * の手続きを実装する上手い書き方がありますでしょうか。 よろしくお願いします。
(define-method + ((x <string>) (y <string>) . z) (apply + (string-append x y) z))しかし、性能上の問題などから一部の演算は特別なフックが仕掛けられている場合があり、 + はそのひとつです。 object-+ にメソッドを追加するとただ + に追加するよりは速いかもしれません。
(define-method object-+ ((a <string>)(b <string>)) (string-append a b))残念ながらこの方法では、 + は可換であることを利用した最適化がかかる場合があるとのことで、順序が意味を持つ文字列の連結に使うと意図しない挙動になるかもしれません。 また、 object-+ にメソッドを追加するのは現状ではドキュメントに載っていない隠し機能です。
hateman? ありがとうございます。 確かに define-method に apply すればいいだけでした。。初歩的ですいません。 また object-+ の機能についてもご説明ありがとうございます。
takker? macroexpand を再帰的な評価をすることは可能なのでしょうか。 例えば以下のような、 my-or を macroexpand を行うと最後まで評価されずに終わってしまいます。
(define-macro (my-or . args) (if (null? args) #f (let ((sym (gensym))) `(let ((,sym ,(car args))) (if ,sym #t (my-or ,@(cdr args)))))))
クロージャーなどでは、macroexpand-all などが存在するようなのですが これを自分で定義することは可能なのでしょうか。 いろいろ調べてもよく分からなかったので質問しました。
(let ((var init) ...) body ...)
を見たら、再帰するのはinitとbody部分、
というようなルールを自分で書いてやる必要がある)、二度手間っぽいことにはなります。
Gaucheのコンパイラは特殊形式の扱い方を知ってるわけですから。macroexpand-all
が使えるようになっています。返り値の仕様はいじるかもしれません。
(define (macroexpand-rec macro form) (cond [(null? form) '()] [(not (pair? form)) form] [(eq? macro (car form)) (macroexpand-rec macro (macroexpand form))] [else (cons (macroexpand-rec macro (car form)) (macroexpand-rec macro (cdr form)))])) (define-macro (%macroexpand-rec macro form) `(macroexpand-rec (quote ,macro) (quote ,form))) ;; マクロ展開 (%macroexpand-rec my-or (my-or (eq? 5 5) (eq? 3 4)))
(foo my-or x)という式だった場合、(cdr form)が(my-or x)になって展開されちゃうんで、
(map (lambda (x) (macroexpand-rec macro x)) form)とするのが定石ですね。ただこの場合、formが完全なリストであることを確認しておく必要があります。
(lambda (my-or x) body ...)とか
(let ((my-or x)) body ...)なんてのが出てきた場合の(my-or x)の部分は展開しちゃいけないし、bodyの中でもmy-orは (マクロをシャドウする)単なる変数になってるので展開できない、といった問題が出てくるということです。 完全にやろうとするとそこそこ面倒です。
asada? 2014/03/21 07:51:48 UTC
gosh -r7で起動した時,Gaucheの既存のprocedureをimportするには、どうすれば良いですか?regexp?等の正規表現が使いたいのです。
(import (gauche regexp))としたのですが、意図通りにimportできてない様です。 お手数ですが、アドバイスを頂けると助かります。
(import (gauche base))
とすれば、
Gaucheの組み込み関数が使えるようになります。
autoloadされる関数も使えるようになるので、Gaucheモードでuse無しで使えてる機能は
基本的に(import (gauche base))
だけで使えるようになるはずです。
anon(2014/01/05 02:27:58 UTC): 自分のPC上でWiLiKiを使いたいと思って、Apacheをインストールして
#!gosh.exe (display "Content-type: text/plain\n\n") (display (gauche-version)) (display "\n") (display (gauche-architecture)) (display "\nHello World")
というCGIスクリプト(localhost/cgi-bin/test.scm.cgi)が
0.9.3.3 i686-pc-mingw32 Hello World
を返すのを確認した後、DOSプロンプト上でWiLiKiをインストールしようとしたところ、
D:\>gauche-package install WiLiKi-0.6.2.tgz *** SYSTEM-ERROR: cannot find program '#f': No error Stack Trace: _______________________________________ 0 (sys-fork-and-exec (car argv) argv :iomap iomap :directory dir :si ... At line 183 of "D:\\Program Files\\Gauche\\share\\gauche-0.9\\0.9.3.3\\lib/gauche/process.scm" 1 (run-process (cond-expand (gauche.os.windows (win-break-cmdargs cm ... At line 51 of "D:\\Program Files\\Gauche\\share\\gauche-0.9\\0.9.3.3\\lib/gauche/package/util.scm" 2 (clean config dir) At line 148 of "D:\\Program Files\\Gauche\\share\\gauche-0.9\\0.9.3.3\\lib/gauche/package/build.scm" 3 (usage) At line 93 of "(input string port)"
となります。 (clean config dir)するためのコマンドが見つからないということなのでしょうか?
2011/10/26 19:49:51 PDT: (touch-file "filename")だと更新日時も変わってしまいます… アクセス日時を変えたいだけなので、実際にファイルを読み込む手間を省く方法はないでしょうか?
(let1 s (sys-stat "filename") (sys-utime "filename" (sys-time) (~ s'mtime))でしょうか。将来的にはtouch(1)コマンドのようなオプションをtouch-fileにつけるのがいいですかね。
2011/07/05 11:41:25 PDT: これ,デフォルトではエラーなんですね.少し引っかかりました.
(ref xs 1.0)
2011/07/05 19:16:14 PDT: Cプログラムを何も考えずにSchemeに移植していたのですが、こう定義すると
gosh> (define-method ref ((l <list>) (r <real>)) (ref l (inexact->exact r)) ) #<generic ref (10)>
これらは動きます
gosh> (ref '(a b) 1.0) b gosh> (~ '(a b) 1.0) b
これはエラーになってしまいます
gosh> (set! (~ '(a b) 1.0) 1) *** ERROR: no applicable method for #<generic |setter of ref| (6)> with argumen s ((a b) 1.0 1) Stack Trace: _______________________________________
さらにこう定義して期待通りの動作となりましたが、正しいのかわかりません
(define-method (setter ref) ((l <list>) (r <real>) obj) (let1 i (inexact->exact r) (set! (ref l i) obj)))
齊藤 (2010/09/29 19:46:49 PDT): syntax-rules のテンプレートについてです。 以下のように書くとエラーになります。
(define-syntax hoge (syntax-rules () ((_ r ...) (list '(r (r ...)) ...)))) (hoge 1 2 3)
期待する展開形は
(list '(1 (1 2 3)) '(2 (1 2 3)) '(3 (1 2 3)))
です。
... が (r ...) にも掛ってしまうことが原因とは思いますが、 R5RS 的には未定義なんでしょうか? 手元で試してみたところ、 Gauche を含む R5RS 処理系のいくつかではエラーになり、主要な R6RS 処理系では期待通りに展開されるようです。
Shiro(2010/10/02 17:33:18 PDT): はい、これはR5RSでは曖昧で、R6RSで明確化されました。
Pattern variables that occur in subpatterns followed by one or more instances of the identifier ... are allowed only in subtemplates that are followed by as many instances of ....
このas manyは「同数の」ですが、文章全体が必要条件を述べているともとれ、その場合 as manyは下限を示していると解釈可能です。テンプレートの方で...が多い場合については 仕様は何も言っていないので、以下のどちらの解釈もOKです。
R6RSでは...が多い場合について明示的に許され、振る舞いも定義されました。
If a pattern variable is followed by more ellipses in the subtemplate than in the associated subpattern, the input form is replicated as necessary.
齊藤 (2010/10/08 21:35:41 PDT) : なるほど。 納得しました。 R5RS 的には使わない方がよさそうですね。
osn(2010/05/27 22:16:11 PDT): 実数の write での出力では、桁数に応じて固定小数点表記あるいは浮動小数点表記とが切り替わります。
gosh> (write 1.0) 1.0 gosh> (write 10000000.0) 10000000.0 gosh> (write 100000000000.0) 1.0e11
この表記の切り替わりの桁数や浮動小数点表記内容をカスタマイズできないでしょうか。write-object でできないかと思い、以下のようなことをやってみたのですが、効果ありませんでした。
gosh> (define-method write-object ((obj <real>) out) (format out "~a*1000" (/ obj 1000))) #<generic write-object (3)> gosh> (write 3e3) 3000.0 (3*1000 と表示されることを期待)
他処理系で生成されたS式のファイルを gauche で読み込んで write とすると数の表記が変わってしまい、できれば元の処理系と同じ規則を定義して出力したいと思っています。 よろしくお願いします。
(define-method write ((obj <real>) . rest) (let-optionals* rest ((out (standard-output-port))) (format out "~d*1000" (inexact->exact (/ obj 1000)))))
gosh> (write 3e3) 3*1000でも、 Scheme では write して read したら同じものになるのが基本なので、こういうカスタマイズはスジが悪い方法かも。 別の名前で専用の関数 (メソッド) を用意した方が無難に思えます。
osn(2010/04/14 20:15:08 PDT):
(便乗したようなタイトルですいません。;)
openSUSE 11.1,11.2(gcc-4.4.1) に Gauche-0.8.14,0.9 をインストールしようとしたところ、./configure の結果では、
optional modules: odbm ndbm gdbm zlib
となっているのに、コンパイル終了時点で、lib/dbm には fsdbm.scm のみしかなく、gdbm インターフェースが生成されないようです。
config.log をみると
configure:11795: checking for dbm_open configure:11851: gcc -std=gnu99 -o conftest -g -O2 conftest.c -ldl -lcrypt - lutil -lm -lpthread >&5 /tmp/ccEbKcoc.o: In function `main': /root/Gauche-0.9/conftest.c:135: undefined reference to `dbm_open' ... configure:12871: checking for dbminit in -ldbm configure:12906: gcc -std=gnu99 -o conftest -g -O2 conftest.c -ldbm -ldl -l crypt -lutil -lm -lpthread >&5 /usr/lib64/gcc/x86_64-suse-linux/4.4/../../../../x86_64-suse-linux/bin/ld: canno t find -ldbm collect2: ld returned 1 exit status ...
等が見られるので、dbm インターフェースに関連して、gauche のコンパイルに失敗しているのかも、と想像しているのですが、対策が分からず困ってます。openSUSE 10.x (gcc-4.1.0)等では問題なくインストールできているのですが、、、対応方法をご教示いただけるとありがたくよろしくお願いします。(gdbm は 1.8.3 で、gauche のインストール可否にかかわらず同バージョンです。)
make[2]: Entering directory `/root/Gauche-0.9/ext/dbm' ../../src/gosh -ftest ../../src/precomp -e -o dbm--odbm odbm.scm gcc -std=gnu99 -DHAVE_CONFIG_H -I. -I../../src -I../../gc/include -g -O2 -fPI C -fomit-frame-pointer -c dbm--odbm.c odbm.scm: In function 'dbm__odbmodbm_close': odbm.scm:212: error: too few arguments to function 'dbmclose' make[2]: *** [dbm--odbm.o] Error 1 make[2]: Leaving directory `/root/Gauche-0.9/ext/dbm'
/* Determine if the C(++) compiler requires complete function prototype */ #ifndef __P #if defined(__STDC__) || defined(__cplusplus) || defined(c_plusplus) #define __P(x) x #else #define __P(x) () #endif #endif ... extern int dbmclose __P((DBM *));となってました。#define __P(x) x が有効になって、dbmclose の引数の数があわない、ということになってるということですね。
--- odbm.scm (revision 7085) +++ odbm.scm (working copy) @@ -207,7 +207,7 @@ (result r))) (define-cproc odbm-close () ::<void> - (when odbm_opened (dbmclose) (set! odbm_opened FALSE))) + (when odbm_opened (dbmclose NULL) (set! odbm_opened FALSE))) (define-cproc odbm-closed? () ::<boolean> (result (not odbm_opened)))
WiLiKi のデータベースに gdbm を使っています。 FreeBSD 8.0 で,portupgrade databases/gauche-gdbm に失敗します。 0.9 がBROKENになっているためです。 試しに,Makefile 中の BROKEN をコメントにして make してみると, gdbm.stub がないと怒られます。 0.8.13 のgdbm.stub をコピーしてmakeすると, 実行時エラーになります。
gdbm を使うのはおすすめではないのでしょうか?
対処方法とおすすめのデータベース形式を教えていただけませんか?
suzuki (2010/03/22 14:40 jst) Gauche-0.9 のソースからインストールしました。FreeBSD 8.0-Release です。下記二ヶ所で悩みました。
suzuki (2010/03/22 14:40 jst) FreeBSD の gauche-gdbm-0.9が BROKEN状態になっていて、Mark BROKEN with 0.9 updateと記載されています。Makefile は 0.8.13 のものと同じで,ソースディレクトリは,Gauche-0.9/ext/dbm をさしています。
0.9 にまだ対応していないものと思いましたが。。。
ziro (2010/03/19 00:53:38 PDT) 勉強のためにいろんな文字列のアルゴリズムを Gauche で書いています.文字列の各文字を順番に見るときには,マルチバイト文字列上でのインデックスによるアクセスは遅いので,string port を使ったほうがいいんですよね.そうして文字列を舐めている途中の二つの状態の間に対応する部分文字列を取得する方法はありますか? 今は port から読んだ文字を別の string port に書いているのですが,どうにも格好がつきません.
Shiro(2010/03/19 04:43:06 PDT): 「遅い」といっても程度の問題で、データのサイズや必要な 性能要件によって変わってきます。インデックスアクセスは入力文字列長に比例するので、 特に問題になるのは「長い文字列に対して頻繁にインデックスアクセスする」という場合です。
従って、(dotimes (n len) (do-something (string-ref str n))) のようなコードは O(N^2)に なってしまいますが、もし取得する部分文字列の総数が定数ならば、
でもいけるかもしれません。
入力文字列が巨大で、substringで深くインデックスするのを避けたいという場合は、 部分文字列の先頭になり得る箇所で、get-remaining-input-stringで半部分文字列 (その箇所以降の文字列)を取得しておく、という手もあります。Gaucheでは 文字列本体は共有されるので、get-remaining-input-stringが返す文字列は 事実上入力文字列の途中を指すポインタみたいなもので、たくさん取得しておいても さほど性能に影響は出ません。さらにマッチングを進めて、切り取るべき文字列の 終端が分かったら、get-remaining-input-stringで取った文字列partial-strに対して (substring partial-str 0 (- end-index start-index)) のようにして切り取ります。 string-takeでもいいです。この場合、文字列のインデックスアクセスのオーバヘッドは 部分文字列の長さに制限され、入力文字列の大きさには影響を受けません。
気になるのがどちらかというと性能よりもコードの簡潔さ、読みやすさだというのなら、 切り取る部分は文字のリストにしといて後でlist->stringするのがたぶんエレガントになると 思います。オーバヘッドはありますがO(N)なのでこれでもstring-refで舐めるよりは ずっと良いと思います。
ziro (2010/03/19 07:26:51 PDT) ありがとうございます.安直には,バイト数ベースの添字で substring ができると簡単だと思いますが,そんなことはできないのでしょう か (incomplete string を使えばできる?).それから,文字列の途中から逆向 きに辿ってみたいと思うのですが,添字を使わないですます方法はありますか?
Shiro(2010/03/19 12:47:08 PDT): これ以上は目的がわからないと何とも言えないのですが、 かなり性能的にシビアな状況なのでしょうか。
substringをincomplete stringに適用すれば(今のところ)バイトインデックスとして 扱われますが、文字インデックスからバイトインデックスを簡単に求める方法が無いので 使いどころが難しいですね。string-portで読み出しつつport-tellでオフセットを記録するって 手はあります (ただし、peek-charするとオフセットがずれるというバグがあるので注意)。
とにかく今、性能が欲しいということでしたら、unofficialなstring-pointerという のがあるにはあります。途中から逆向きということができます。ただし将来無くなるかも しれません。
あとは、アルゴリズム的にインデックスアクセスが綺麗に書ける、ということなら いっそベクタに変換してしまうとか。 綺麗さにこだわるなら読んだ文字を全部リストにしとくとか、 文字列から離れる手もあります。
ziro (2010/03/20 05:24:03 PDT) 私の状況は要件が厳しいというものではなく,毎回再計算が起こるから string-ref は軽い気持ちでは使い難いという程度のものです.紹介して下さった方法の一で書いて実際の文字列に適用してみて,様子を見たいと思います.まともに可変バイト長の文字エンコーディングを扱えるのはとても有り難いことなので,うまく Gauche を使えたらと思います.ありがとうございました.
Shiro(2010/03/20 07:15:28 PDT): それなら、とりあえず
というのが、多分シンプルさと性能のバランスのいいところじゃないかと思います。 それで問題が出てきたら上に挙げた他の方法を試すということで。