関数型言語の勉強にSICPを読もう - (39) 3章 - 小休止 ファイルI/Oと正規表現

実用っぽいコードを書かないと中々上達しないと思うので無理やり書いてみました。
引数で受け取ったファイルを開いて、ファイル内を置換する。
sedとかPerlならすぐに書けるよ」とか「もっと汎用化したスクリプトを書いたほうが良い」というのは分かるのですがまずは練習ということで。


まだ完成していなくて、現在分かっている問題点は

  • 入力と出力ファイルを同じにするとおかしくなる
  • 結果文字列が""で囲まれてしまう
  • 改行コードがなくなってしまう

などなど。
気長に直していこう。

#!/usr/bin/env gosh

(use file.util)

(define (main args)
  (define (replace-text file)
    (let ((lines (file->string-list file))
          (result ""))
      (for-each
       (lambda (line)
         (set! result (string-append result (regexp-replace #/^(www\.example\.com\s+).+/ line "\\1 192.168.0.1"))))
       lines)
    result))
  (if (null? (cdr args))
      (error "no args")
      (let1 text (replace-text (cadr args))
            (call-with-output-file
                "hoge.txt";;(cadr args)
              (lambda (port)
                (write text port))))))

追記

SaitoAtsushiさんとRuiさんからアドバイスを頂き、書き直してみました。
劇的ビフォーアフター

#!/usr/bin/env gosh

(use file.util)

(define (main args)
  (define (replace-text file)
    (map
     (lambda (line)
       (regexp-replace #/^(www\.example\.com\s+).+/ line "\\1 192.168.0.1"))
     (file->string-list file)))
  (if (null? (cdr args))
      (error "no args")
      (let1 lines (replace-text (cadr args))
            (call-with-output-file
                (cadr args)
              (lambda (port)
                (for-each
                 (lambda (line)
                   (display line port)
                   (newline port))
                 lines))))))
1. resultへの代入をなくす

resultにset!しているところは、for-eachの代わりにmapを使って代入を避けることができますね。文字列ではなく文字列のリストを返すようにするのがいいです。

(file->string-list file)の結果に、mapするのですね。かっこいい!。

2.入力と出力ファイルを同じにするとおかしくなる

Ruiさんのご指摘のとおり、call-with-output-file のなかで file->string-listを呼んでいるのが原因でした。
let1であらかじめ評価するようにすればOKです。

3.結果文字列が””で囲まれてしまう

writeはデータをS式として出力するようになってます。
単なるテキストとして出力したいならdisplay関数で出力しないといけません。

displayを使うようにしました。

4.改行コードがなくなってしまう

Perlの<>などは改行を残しますが、Gaucheのread-lineが返す文字列に行末の改行文字は含まれません。STkのread-lineもそういう動作のようです。なぜ改行を残さないのか正確な理由はわかりませんが、想像するに、改行文字は行というレコードのセパレータであって、行そのものではないからじゃないかなぁ。Shiroさんがなにか解説してくれるかも。

(newline port)を利用するようにしました。


というわけでだいぶきれいになりました。ありがとうございます。
mapで代入が減らせたのがかなりうれしいです。


やっぱり書いてみるといろいろなことが分かりますね。
lambdaへの心理的抵抗がゼロになりました。
すんなりとここは lambda だよねと思えるようになった。
Schemeをはじめる前は、「Boostで lambdaできるんだぜ」とか言われると「Boostで編み物ができるらしいよ」ってのと同じくらいイメージが沸かなかったんだけどうれしいなぁ。


※「SICPを読もう」の目次はこちら


計算機プログラムの構造と解釈
Gerald Jay Sussman Julie Sussman Harold Abelson 和田 英一
ピアソンエデュケーション (2000/02)
売り上げランキング: 56,404