HOME | 12. シンボル型 | もうひとつの Scheme 入門 | 14. ベクトルと構造体 | download | 書き込む |
球団名 | ホームグラウンド名 |
---|---|
千葉ロッテマリーンズ | 千葉マリンスタジアム |
福岡ソフトバンクホークス | 福岡Yahoo!JAPANドーム |
西武ライオンズ | インボイスSEIBUドーム |
オリックス・バファローズ | 大阪ドーム |
北海道日本ハムファイターズ | 札幌ドーム |
東北楽天ゴールデンイーグルス | フルキャストスタジアム宮城 |
阪神タイガース | 阪神甲子園球場 |
中日ドラゴンズ | ナゴヤドーム |
横浜ベイスターズ | 横浜スタジアム |
ヤクルトスワローズ | 明治神宮野球場 |
読売ジャイアンツ | 東京ドーム |
広島東洋カープ | 広島市民球場 |
連想リストを取り扱う関数は R6RS の List utilities で、 また、ハッシュ表は Hashtables で定義されています。
ハッシュ表は SRFI-69 でも、規定されています。 処理系によっては、R6RS ではなく SRFI に準拠しているのもあるかもしれません。
この文書では R6RS に沿って記述します。
以下は連想リストの例です。 ペアになっていれば必ずしもドット対でなくても差し支えありません。
'((hi . 3) (everybody . 5) (nice . 3) (to . 10) (meet . 4) (you . 8)) '((1 2 3) (4 5 6) (7 8 9))
連想リストを検索する関数には、 assq, assv, assoc , assp (R6RS のみ) があります。 これらの関数は連想リストを逐次検索し、 car 部がキーと等しいペアを返します。ペアがない場合は #f が返ります。 また、assq, assv, assoc はキーの比較にそれぞれ eq?, eqv?, equal? を使用しています。 また、assp は car 部を proc で処理し、#f にならないものを返します。 従って、assq が最も高速に検索でき、assoc が最も低速になります。 このことから文字列、ベクトル、リストはシンボルや数値に変換してからキーにしたほうが速度的に有利です。 また、多くのデータを検索するときは後述する ハッシュ表を利用したほうが高速です。
以下に例を示します。
(require rnrs/lists-6) (define wc '((hi . 3) (everybody . 5) (nice . 3) (to . 10) (meet . 4) (you . 8))) ⇒ wc (assq 'hi wc) ⇒ (hi . 3) (assq 'you wc) ⇒ (you . 8) (assq 'i wc) ⇒ () (define n '((1 2 3) (4 5 6) (7 8 9))) ⇒ n (assv 1 n) ⇒ (1 2 3) (assv 8 n) ⇒ ()
(make-hashtable equal-hash equal?)で作ります。equal-hash は リストなどからハッシュ値を求める関数です。 R6RS では equal-hash, string-hash などのハッシュ関数が規定されています。 eq-hashtable はアドレスを比較するだけので もっとも高速です。
辞書に載っているようなパスワードだと破られる危険が大きいですし、一方、グラフィック文字を全くランダムに 並べたパスワードは覚えることはおろか、入力も非常に手間取ります。 このプログラムは、部分的にありうる綴りのパスワードを 10 個生成するプログラムです。 パスワードはなるべく頻繁に変えたほうが良いのですが、自分の頭で考えるのはかなり億劫です。 このプログラムを使えば気軽にパスワードを変更することができます。
このプログラムは2つの部分からなり、1つは英語のテキストから文字のつながりの頻度のデータを 生成するプログラム (stat-spell.scm)、もうひとつはそのデータに基づいてパスワードを 作るプログラム (make-pw.scm) です。
ソースは [code 1] の様になります。
[code 1]
001: ;;; make an alist of probable spelling from a given english text 002: 003: 004: ;; モジュールを読み込みます 005: (require rnrs/base-6) ; vector-for-each を使うため 006: (require rnrs/hashtables-6) ; hashtable 007: (require rnrs/control-6) ; when を使うため 008: (require rnrs/files-6) ; ファイルシステム 009: 010: 011: 012: 013: 014: ;; スキップする文字列なら #t を返します 015: (define (skip-char? c) 016: (case c 017: ((#\: #\; #\' #\" #\`) #t) 018: (else (not (char<=? #\Space c #\~))))) 019: 020: 021: 022: ;; filename のファイルから一文字ずつ読み込み、連続する文字のハッシュ表を作ります 023: (define (pw-read-text filename) 024: (let ((char-hash (make-eqv-hashtable))) ; 全体のハッシュ表です 025: (with-input-from-file filename 026: (lambda () 027: (let loop ((c #\Space)) 028: (let ((c1 (read-char))) ; 一文字読み込んで、 029: (when (not (eof-object? c1)) ; EOF でなければ、以下の処理を行う 030: (if (skip-char? c1) ; 031: (loop c) ; スキップする文字なら、スキップして繰り返し。 032: (let ((c1 (char-downcase c1)) ; そうでなければ、小文字に変換し、 033: (h (hashtable-ref char-hash c #f))) ; その文字のあとの文字の頻度のハッシュ表を char-hash から取り出す。 034: (if (hashtable? h) ; 文字 c の後に続く文字のハッシュ表がすでにあれば、 035: (hashtable-set! h c1 (+ 1 (hashtable-ref h c1 0))) ; 次の文字 c1 の頻度に 1 を加える 036: (let ((h1 (make-eqv-hashtable))) ; ハッシュ表がまだなければ作成する。 037: (hashtable-set! h1 c1 1) 038: (hashtable-set! char-hash c h1))) 039: (loop c1)))))))) ; 繰り返し 040: char-hash)) 041: 042: 043: 044: ;; 個々の文字の後ろの文字の頻度を連想リストにして書き出します。 045: (define (write-each c h) 046: (display "(") 047: (write c) 048: (display " ") 049: (let ((n (hashtable-size h))) 050: (let-values (((vec-c vec-count) (hashtable-entries h))) ; ハッシュ表 h から 文字と頻度のベクトル vec-c, vec-count を取り出します。 051: (vector-for-each ; vector にわたって (lambda ... を作用させます。 052: (lambda (k v) ; 文字と頻度を書き出します。 053: (display " (") 054: (write k) 055: (display " . ") 056: (write v) 057: (display ") ")) 058: vec-c vec-count))) 059: (display ")") 060: (newline)) 061: 062: 063: 064: ;; 頻度のハッシュ表 char-hash を連想リストにして書き出します。 065: (define (write-alist filename char-hash) 066: (when (file-exists? filename) ; データファイルがすでにあれば削除します 067: (delete-file filename)) 068: (with-output-to-file filename ; filename のファイルを開いて、 069: (lambda () ; データを書き出していきます。 070: (display "(define *stat-spell* \'(") 071: (newline) 072: (let-values(((keys values) (hashtable-entries char-hash))) 073: (vector-for-each 074: (lambda (k v) (write-each k v)) keys values)) 075: (display "))") 076: (newline)))) 077: 078: 079: ;; テキストファイルを読み込んで、連続する文字の頻度データを作ります。 080: (define (stat-spell input-filename) 081: (write-alist "statspell.dat" (pw-read-text input-filename)))
実行方法:
> (load/cd "stat-spell.scm")
> (stat-spell "avg.txt") ; 英語のテキストファイルを読み込ませて頻度データを作る
以下のようなファイル statspell.dat が生成します。
(define *stat-spell* '(
(#\8 (#\0 . 2) (#\4 . 1) )
.....
(#\k (#\. . 1) (#\, . 2) (#\e . 54) (#\t . 3) (#\s . 10) (#\a . 1) (#\space . 25) (#\n . 18) (#\- . 1) (#\l . 2) (#\i . 24) ) *
.....
))
* の行は、文字 #\k のあとには #\., #\e, #\t, #\s, #\a,
#\space, #\n, #\-, #\l, #\i がそれぞれ[code 2]
001: ;;; make password from the alist of probable spelling 002: 003: (require srfi/27) 004: (require rnrs/hashtables-6) 005: (require rnrs/control-6) 006: 007: 008: 009: (load/cd "statspell.dat") ; *stat-spell* (alist for following characters) is in. 010: 011: 012: (define (alist->hash al) 013: (let ((h (make-eqv-hashtable))) 014: (for-each (lambda (p) 015: (hashtable-set! h (car p) (cdr p))) 016: al) 017: h)) 018: 019: 020: 021: (define (pw-random-select vec) 022: (vector-ref vec (random-integer (vector-length vec)))) 023: 024: (define (random00) 025: (let loop ((i 0) (acc '())) 026: (if (= i 2) 027: (list->string acc) 028: (loop (+ 1 i) (cons (pw-random-select '#(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)) acc))))) 029: 030: (define (occasional-upcase c) 031: (if (< (random-integer 10) 3) 032: (char-upcase c) 033: c)) 034: 035: (define (pw-enhance ls) 036: (list->string 037: (map (lambda (c) 038: (cond 039: ((char=? c #\Space) 040: (pw-random-select '#(#\- #\_ #\/ #\Space #\. #\, #\@ #\? #\( #\)))) 041: ((char-alphabetic? c) 042: (occasional-upcase c)) 043: (else c))) 044: (cdr (reverse ls))))) 045: 046: 047: (define (random-following alist) 048: (let ((n (random-integer (apply + (map cdr alist))))) 049: (let loop ((j 0) (alist alist)) 050: (when (pair? alist) 051: (let* ((pair (car alist)) 052: (k (+ j (cdr pair)))) 053: (if (> k n) 054: (car pair) 055: (loop k (cdr alist)))))))) 056: 057: (define (make-pw h n) 058: (let loop ((i 0) (c #\Space) (acc '())) 059: (if (= i n) 060: (string-append 061: (pw-enhance (cons #\Space (cons c acc))) 062: (random00)) 063: (loop (+ 1 i) 064: (random-following (hashtable-ref h c '((#\Space . 1)))) 065: (cons c acc))))) 066: 067: (define (pw-candidates) 068: (let loop ((i 0)) 069: (when (< i 10) 070: (display i) 071: (display ": ") 072: (write (make-pw (alist->hash *stat-spell*) (+ 9 (random-integer 4)))) 073: (newline) 074: (loop (+ 1 i))) 075: 'done))
> (load/cd "make-pw.scm")
> (pw-candidates)
0: "S.bowAtof.t_)13"
1: "igIf@t)yO?61"
2: "icOFEAnly?98"
3: "prabER)Y_in(96"
4: "He@lat,Ms@74"
5: "SrtoRepASti)96"
6: "y?s)pmP m,_-52"
7: "tEmm(kIo)sT@51"
8: "AilITheRviS/.08"
9: "TObe-INy,,p 86"
done
>
HOME | 12. シンボル型 | もうひとつの Scheme 入門 | 14. ベクトルと構造体 | download | 書き込む |