Hatena::Groupsicp

a666666の日記 このページをアンテナに追加 RSSフィード

2009-12-19

1.16. Soundex Matching

13:30 |  1.16. Soundex Matching - a666666の日記 を含むブックマーク はてなブックマーク -  1.16. Soundex Matching - a666666の日記  1.16. Soundex Matching - a666666の日記 のブックマークコメント

http://docstore.mik.ua/orelly/perl/cookbook/ch01_17.htm

英単語の「あいまい検索」を実装しろ、という話かな? Text::Soundex というモジュールを使えといってる。ハイ無理。いちおう Text::Soundex のソースを読んでみたけど XS 使ってた。アルゴリズムを真似て Gauche 版の text.soundex を作れたらすげーけど、とてもそんなレベルじゃないのでパス。いまはもっとレベル低いことからやる。

しかしパスとかできないとか増えてきたな。。

1.17. Program: fixstyle

14:30 |  1.17. Program: fixstyle - a666666の日記 を含むブックマーク はてなブックマーク -  1.17. Program: fixstyle - a666666の日記  1.17. Program: fixstyle - a666666の日記 のブックマークコメント

http://docstore.mik.ua/orelly/perl/cookbook/ch01_18.htm

だいぶ応用編になってきてる予感。。つらい。

Perl

#!/usr/bin/perl -w
# fixstyle - switch first set of <DATA> strings to second set
#   usage: $0 [-v] [files ...]
use strict;
my $verbose = (@ARGV && $ARGV[0] eq '-v' && shift);

if (@ARGV) {
    $^I = ".orig";          # preserve old files
} else {
    warn "$0: Reading from stdin\n" if -t STDIN;
}

my $code = "while (<>) {\n";
# read in config, build up code to eval
while (<DATA>) {
    chomp;
    my ($in, $out) = split /\s*=>\s*/;
    next unless $in && $out;
    $code .= "s{\\Q$in\\E}{$out}g";
    $code .= "&& printf STDERR qq($in => $out at \$ARGV line \$.\\n)" 
                                                        if $verbose;
    $code .= ";\n";
}
$code .= "print;\n}\n";

eval "{ $code } 1" || die;

__END__
analysed        => analyzed
built-in        => builtin
chastized       => chastised
commandline     => command-line
de-allocate     => deallocate
dropin          => drop-in
hardcode        => hard-code
meta-data       => metadata
multicharacter  => multi-character
multiway        => multi-way
non-empty       => nonempty
non-profit      => nonprofit
non-trappable   => nontrappable
pre-define      => predefine
preextend       => pre-extend
re-compiling    => recompiling
reenter         => re-enter
turnkey         => turn-key


とりあえず、 __DATA__ の単語リストを連想リストにすればよさそうだなと思って連想リストをつくるのを Gauche で書いてみようとしたけどどうにもうまくいかず。仕方ないので Perlフィルタを書いた。で、なんか単純に連想リストをキーで引いて値を返すだけのプログラムは書けた。

Gauche

(define alist
  '(
    (analysed . analyzed)
    (built-in . builtin)
    (chastized . chastised)
    (commandline . command-line)
    (de-allocate . deallocate)
    (dropin . drop-in)
    (hardcode . hard-code)
    (meta-data . metadata)
    (multicharacter . multi-character)
    (multiway . multi-way)
    (non-empty . nonempty)
    (non-profit . nonprofit)
    (non-trappable . nontrappable)
    (pre-define . predefine)
    (preextend . pre-extend)
    (re-compiling . recompiling)
    (reenter . re-enter)
    (turnkey . turn-key)))

(define (filter word alist)
  (cdr (assoc word alist)))

(filter 'analysed alist)

うーん、どうしようもない。 eval もあるから、文字列でコードを組み立ててどうたら、っていう問題の例どおりにすることもたぶんできるとおもうけど、まぁ、、先へ進もう。

1.18. Program: psgrep

15:26 |  1.18. Program: psgrep - a666666の日記 を含むブックマーク はてなブックマーク -  1.18. Program: psgrep - a666666の日記  1.18. Program: psgrep - a666666の日記 のブックマークコメント

http://docstore.mik.ua/orelly/perl/cookbook/ch01_19.htm

パス!!1 ・・・この次のページは二章だったので簡単そうなのから順次やります。。

2.1. Checking Whether a String Is a Valid Number

15:29 |  2.1. Checking Whether a String Is a Valid Number - a666666の日記 を含むブックマーク はてなブックマーク -  2.1. Checking Whether a String Is a Valid Number - a666666の日記  2.1. Checking Whether a String Is a Valid Number - a666666の日記 のブックマークコメント

http://docstore.mik.ua/orelly/perl/cookbook/ch02_02.htm

数字っぽい文字列かどうかを判定するという問題。正規表現でなんかいろいろやってる。しかし Perl でやるにしても何かモジュールあった気がするなぁ。

Perl

warn "has nondigits"        if     /\D/;
warn "not a natural number" unless /^\d+$/;             # rejects -3
warn "not an integer"       unless /^-?\d+$/;           # rejects +3
warn "not an integer"       unless /^[+-]?\d+$/;
warn "not a decimal number" unless /^-?\d+\.?\d*$/;     # rejects .2
warn "not a decimal number" unless /^-?(?:\d+(?:\.\d*)?|\.\d+)$/;
warn "not a C float"
       unless /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/;

で、 Gauche でやるならやっぱり組み込みの手続きがたくさんあるのでそれを使ったほうがよさそうだ。

Gauche

(number? x)

これだけ。。

2.2. Comparing Floating-Point Numbers

17:44 |  2.2. Comparing Floating-Point Numbers - a666666の日記 を含むブックマーク はてなブックマーク -  2.2. Comparing Floating-Point Numbers - a666666の日記  2.2. Comparing Floating-Point Numbers - a666666の日記 のブックマークコメント

http://docstore.mik.ua/orelly/perl/cookbook/ch02_03.htm

浮動小数点数の比較をしろ、ということのようなのだが・・・。 accuracy は精度って意味。小数点以下何桁目まで、同じ数字なのか、というのを調べるルーチンのようだ。

Perl

# equal(NUM1, NUM2, ACCURACY) : returns true if NUM1 and NUM2 are
# equal to ACCURACY number of decimal places

sub equal {
    my ($A, $B, $dp) = @_;

    return sprintf("%.${dp}g", $A) eq sprintf("%.${dp}g", $B);
  }

正直、精度とかさっぱりわからないので、

Gauche

(= x y)

ではだめなのですか?ってかんじです。。

=> 2.3 が解けた?のでこっちもやり直し。 2.3 で書いた myround を使った。 my= のなかで比較関数eq? を使ってしまって、 (eq? 3.14 3.14) が #f になるのはなんで?と思ってしまった(浮動小数点数は不正確数なので eq? で比較したら #f になるものらしい)

Gauche

(use math.const)

(define (myround x accurary)
  (let ((decimal (expt 10 accurary))) ; decimal => 10^accurary
    #?=(/ (round (* x decimal)) decimal)))

(define (my= x y accurary)
  (= (myround x accurary) (myround y accurary)))

(my= 3.14 pi 2)
; => #t
(my= 3.14 pi 3)
; => #f

2.3. Rounding Floating-Point Numbers

18:05 |  2.3. Rounding Floating-Point Numbers - a666666の日記 を含むブックマーク はてなブックマーク -  2.3. Rounding Floating-Point Numbers - a666666の日記  2.3. Rounding Floating-Point Numbers - a666666の日記 のブックマークコメント

http://docstore.mik.ua/orelly/perl/cookbook/ch02_04.htm

浮動小数点数を丸めなさい、という話。

うーん。。。わかりません。。。 => いやいやもうちょっと頑張れ俺、ということで書いてみました。

Perl

$a = 0.255;
$b = sprintf("%.2f", $a);
print "Unrounded: $a\nRounded: $b\n";
printf "Unrounded: $a\nRounded: %.2f\n", $a;

Unrounded: 0.255
Rounded: 0.26
Unrounded: 0.255
Rounded: 0.26

もはやコーディングの練習じゃなくてリファレンスマニュアルリーディングになりつつある。該当しそうな項目を読んでちょろっと一行くらいコードを書いてみるだけ(当然例題を解けるような分量ではない)

Gauche

(use math.const)

(define (myround x accurary)
  (let ((decimal (expt 10 accurary))) ; decimal => 10^accurary
    (/ (round (* x decimal)) decimal)))

pi
; => 3.141592653589793
(myround pi 7)
; => 3.1415927
(myround pi 3)
; => 3.142

2009-12-14

1.13. Escaping Characters

23:13 |  1.13. Escaping Characters - a666666の日記 を含むブックマーク はてなブックマーク -  1.13. Escaping Characters - a666666の日記  1.13. Escaping Characters - a666666の日記 のブックマークコメント

http://docstore.mik.ua/orelly/perl/cookbook/ch01_14.htm

% とか " とかをエスケープしろと。正規表現で置換してるだけ。

Perl

$string =~ s/%/%%/g;

$string = q(Mom said, "Don't do that.");
$string =~ s/(['"])/\\$1/g;

http://practical-scheme.net/gauche/man/gauche-refj_108.html#SEC321

srfi-13 のドキュメントなどを読んでみたけどさすがにそういう関数は見つからず。素直に正規表現でやることにする。 regexp-replace-all とかかな。

http://practical-scheme.net/gauche/man/gauche-refj_49.html#SEC86

Gauche

(regexp-replace-all #/%/ "%" "%%")

(regexp-quote "Mon said, \"Don't do that.\"")

(regexp-replace-all #/(['\"])/ (regexp-quote "Mon said, \"Don't do that.\"") "\\1")
; gosh> *** ERROR: submatch index out of range: 1
; Stack Trace:

(regexp-replace-all #/(['\"])/ (regexp-quote "A'B\"C") "\\1")

うーん。。簡単にできると思ったのに、なんかエラーになってしまう。なんだろうなぁ。 regexp-quote を使ったほうが良いということはわかったが、これでもまだ足りないような。

単に () でグループ化してないせいだった。しかし、今度は置換がされてないという...。ていうか元々置換できちゃってるしなぁ。

(define (escape str)
  (regexp-replace-all #/(['\"])/ str '"\\1"))
(escape "Mon said, \"Don't do that.\"")

置換文字列のほうの指定を間違ってた...。しかし、こんどはバックスラッシュが。 \\\\1 ってすると \\1 って文字列に置換されてしまう(\ + $1 に置換したいのだが)。

難しいなぁ。 Gauche 正規表現 置換 などですこしぐぐってみたけどこれ以上は無理そうなので諦める。

1.14. Trimming Blanks from the Ends of a String

23:23 |  1.14. Trimming Blanks from the Ends of a String - a666666の日記 を含むブックマーク はてなブックマーク -  1.14. Trimming Blanks from the Ends of a String - a666666の日記  1.14. Trimming Blanks from the Ends of a String - a666666の日記 のブックマークコメント

http://docstore.mik.ua/orelly/perl/cookbook/ch01_15.htm

文字列の前後のホワイトスペースを削除しろという問題。これは楽勝か...? 破壊的なのと非破壊的なのの二種類ある。

Perl

# 破壊的
$string =~ s/^\s+//;
$string =~ s/\s+$//;

# 非破壊的
$string = trim($string);
@many   = trim(@many);

sub trim {
    my @out = @_;
    for (@out) {
        s/^\s+//;
        s/\s+$//;
    }
    return wantarray ? @out : $out[0];
}

Gauche

(use srfi-13)

(define string1 "  __END__  ")
(string-trim string1)
(string-trim-right string1)
(string-trim-both string1)

(define string2 "  __END__  ")
(set! string2 (string-trim string2))
string2 ; => "__END__  "

srfi-13 を使えば簡単。いちおう非破壊的なのは set! を使ってやってみた。

http://practical-scheme.net/gauche/man/gauche-refj_25.html#SEC32

1.15. Parsing Comma-Separated Data

00:16 |  1.15. Parsing Comma-Separated Data - a666666の日記 を含むブックマーク はてなブックマーク -  1.15. Parsing Comma-Separated Data - a666666の日記  1.15. Parsing Comma-Separated Data - a666666の日記 のブックマークコメント

http://docstore.mik.ua/orelly/perl/cookbook/ch01_16.htm

CSVパース。複雑な正規表現が書いてあるけど、こんなのは素直にライブラリを使ったほうがよろしい。 Gauche にも text.csv モジュールが添付されているのそれを使う。

Perl

sub parse_csv {
    my $text = shift;      # record containing comma-separated values
    my @new  = ();
    push(@new, $+) while $text =~ m{
        # the first part groups the phrase inside the quotes.
        # see explanation of this pattern in MRE
        "([^\"\\]*(?:\\.[^\"\\]*)*)",?
           |  ([^,]+),?
           | ,
       }gx;
       push(@new, undef) if substr($text, -1,1) eq ',';
       return @new;      # list of values that were comma-separated
}  

use Text::ParseWords;

sub parse_csv {
    return quoteword(",",0, $_[0]);
}

$line = q<XYZZY,"","O'Reilly, Inc","Wall, Larry","a \"glug\" bit,",5,
    "Error, Core Dumped">;
@fields = parse_csv($line);
for ($i = 0; $i < @fields; $i++) {
    print "$i : $fields[$i]\n";
}
0 : XYZZY
1 :
2 : O'Reilly, Inc
3 : Wall, Larry
4 : a \"glug\" bit,
5 : 5
6 : Error, Core Dumped

ちょっと見た目も整形してみる。 gauche.sequence モジュールにある map-with-index とかを使うと、インデックス数も受け取れる。

Gauche

(use text.csv)
(use gauche.sequence)

(define reader (make-csv-reader #\,))
(reader (open-input-string "\"Hello,World!\",\"Goodbye,World!\""))
(reader (open-input-string "XYZZY,\"\",\"O'Reilly, Inc\",\"Wall, Larry\",\"a \\\"glug\\\" bit,\",5,\"Error, Core Dumped\""))

(display
 (string-join
  (map-with-index
   (lambda (i x)
     (format "~D : ~S" i x)
     )
   (reader (open-input-string "XYZZY,\"\",\"O'Reilly, Inc\",\"Wall, Larry\",\"a \"glug\" bit,\",5,\"Error, Core Dumped\"")))
 "\n"))

gosh> 0 : "XYZZY"
1 : ""
2 : "O'Reilly, Inc"
3 : "Wall, Larry"
4 : "a "
5 : ",5,"
6 : "Core Dumped\""#<undef>

うーん、だいたいあってると思うんだけど、文字列リテラル中でのエスケープがうまくできてない。"\" => " 一文字まではわかるんだけど、 " ... " のなかで \" (バックスラッシュ + ダブルクォート) をどう表現したらうまくいくのかよくわからない。ダブルクォート以外での文字列リテラルの表記方法があればなぁ。

open-input-port

http://practical-scheme.net/gauche/man/gauche-refj_57.html#SEC117

text.csv

http://practical-scheme.net/gauche/man/gauche-refj_150.html#SEC450

gauche.sequence

http://practical-scheme.net/gauche/man/gauche-refj_91.html

2009-12-09

1.12. Reformatting Paragraphs

00:29 |  1.12. Reformatting Paragraphs - a666666の日記 を含むブックマーク はてなブックマーク -  1.12. Reformatting Paragraphs - a666666の日記  1.12. Reformatting Paragraphs - a666666の日記 のブックマークコメント

http://docstore.mik.ua/orelly/perl/cookbook/ch01_12.htm

長い文字列を指定の文字数で折り返せ、という問題。 Text::Wrap モジュールを使うらしい。グローバル変数使ってるのかな、なんか微妙なインターフェースだな...

Perl

use Text::Wrap;
@OUTPUT = wrap($LEADTAB, $NEXTTAB, @PARA);

#!/usr/bin/perl -w
# wrapdemo - show how Text::Wrap works

@input = ("Folding and splicing is the work of an editor,",
          "not a mere collection of silicon",
          "and",
          "mobile electrons!");

use Text::Wrap qw($columns &wrap);

$columns = 20;
print "0123456789" x 2, "\n";
print wrap("    ", "  ", @input), "\n";

とりあえず、折り返す文字数と文字列を受け取って、折り返して返すだけの関数を書いてみる。

まだぜんぜんできてない。難しい...

以下のようなアプローチで考えてる。合ってるか不明。

  1. 文字列 str をスペース区切りで文字列のリストに分解する
  2. 文字列のリスト lst を先頭から順番にスペースで結合していく
  3. 結合された文字列の長さをしらべて、折り返す文字数 len よりも大きかったらそれ以上結合せずに、結合済みの文字列 line をどこかへ保存しておいて残りの文字列のリスト (cdr lst) に対して 2. を適用する
  4. 文字列のリスト lst が空リストになったら保存済みの文字列のリスト lines を返して終了

日本語で書いてみるとちょっと整理できたかも。

続 1.12. Reformatting Paragraphs

01:45 |  続 1.12. Reformatting Paragraphs - a666666の日記 を含むブックマーク はてなブックマーク -  続 1.12. Reformatting Paragraphs - a666666の日記  続 1.12. Reformatting Paragraphs - a666666の日記 のブックマークコメント

fold つかって文字列のリストを畳み込みつつ join していけば...とか思ってまたすこしいじってみたけどやっぱりうまくいかず。うーん。もう一晩考えて無理ならスキップしよう。

Gauche

(use srfi-1)
(use srfi-13)
(define text "This is a pen. I am a man. Time waits for no one.")
(define lst (string-split text #/\s/))

(fold (lambda (x y z) (string-join (list x y z) " ")) "" (list (car lst)) (cdr lst))

(define (conc len str lst)
  (print str)
  (cond ((>= (string-length (string-join (list str (car lst)) " ")) len) "")
        (else
         (let ((str2 (string-join (list str (car lst)) " ")))
           (conc len str2 (cdr lst))))))

conc
(string-length (conc 10 "" lst))
        

(define (wrap/string-join lst)
  (string-join lst " "))
(define (wrap len str)
  (define lst (string-split str #/\s/))
  (define lines '())
  (cond ((null? lst) #f)
        ((> (string-length (string-join lst " ")) len) (string-join lst " "))
        (else
         (cons lines (wrap len (cdr lst))))))

(wrap 10 text)

(define (wrap len lst)
  (print len (string-length (string-join lst " ")))
  (let ((part ""))
    (cond ((null? lst) (print "hoge") #f)
          ((> (string-length (string-join (list part (car lst)) " ")) len)
           part)
          (else (wrap len (cdr lst))))))

(print (wrap 10 lst))

続続 1.12. Reformatting Paragraphs

22:09 |  続続 1.12. Reformatting Paragraphs - a666666の日記 を含むブックマーク はてなブックマーク -  続続 1.12. Reformatting Paragraphs - a666666の日記  続続 1.12. Reformatting Paragraphs - a666666の日記 のブックマークコメント

ちょっと突破できなそうなのでパス。。もう少し経験積んでから再挑戦しよう。

2009-12-08

1.10. Interpolating Functions and Expressions Within Strings

00:29 |  1.10. Interpolating Functions and Expressions Within Strings - a666666の日記 を含むブックマーク はてなブックマーク -  1.10. Interpolating Functions and Expressions Within Strings - a666666の日記  1.10. Interpolating Functions and Expressions Within Strings - a666666の日記 のブックマークコメント

http://docstore.mik.ua/orelly/perl/cookbook/ch01_11.htm

Problem

You want a function call or expression to expand within a string. This lets you construct more complex templates than with simple scalar variable interpolation.

文字列リテラル中に任意の式(関数呼び出しなど)を埋め込んで実行時に式の返り値を展開できるか、という意味だと思う。以下の Perl での例だと、 @{[ ... ]} とか ${\( ... )} とかいうキモイテクニックを使ってそういう芸当ができると。

Perl

Solution

You can break up your expression into distinct concatenated pieces:

$answer = $var1 . func() . $var2;   # scalar only

Or you can use the slightly sneaky @{[ LIST EXPR ]} or ${ \(SCALAR EXPR ) } expansions:

$answer = "STRING @{[ LIST EXPR ]} MORE STRING";
$answer = "STRING ${\( SCALAR EXPR )} MORE STRING";

はしょったサンプルコード

Date: @{[ do { my $now = `date`; chomp $now; $now } ]} (today)

Today, you bounced check number @{[ 500 + int rand(100) ]} to us.

Gauche みたいなお行儀の良さそうな言語じゃこういうのは無理なんじゃないかなぁと途方に暮れつつ一応リファレンスマニュアルの文字列の項を読んでいったら、驚くべきことにかなりスマートなシンタックスなのにもかかわらず強烈なやり方があった。

Gauche

(use srfi-19)
(define (random n)
  (modulo (sys-random) n))

#`"Date: ,(date->string (current-date))"

#`"Today, you bounced check number ,(+ 500 (random 100)) to us."

「文字列の補間」というのがあって、 http://practical-scheme.net/gauche/man/gauche-refj_48.html#SEC81 に書いてあるのだけど、マクロでそういうのがあるらしい。 #`"..." という風に文字列リテラルを書いて、カンマに続けて(スペースを空けてはいけない) S 式を書けばいいらしい。なぜカンマなのか、などについては「準クォート」の項がとてもわかりやすかった http://practical-scheme.net/gauche/man/gauche-refj_30.html#SEC37 シングルクォートでのいわゆる (quote hoge) とは逆に、「これはクォートしないよ」と明示する(それ以外は全部クォートする)というのがあるのか。

いやはや、ちょっとびっくり。

余談。 (random) の件は http://masimaro.net/blog/2007/01/gaucherandom_linux.htmlコメント欄でみつけた。よくこういうの思いつくよなぁ。

1.11. Indenting Here Documents

00:37 |  1.11. Indenting Here Documents - a666666の日記 を含むブックマーク はてなブックマーク -  1.11. Indenting Here Documents - a666666の日記  1.11. Indenting Here Documents - a666666の日記 のブックマークコメント

http://docstore.mik.ua/orelly/perl/cookbook/ch01_12.htm

ヒアドキュメントをインデントしろって話。例のコードがなんかもともとインデントがアレなような・・・ヒアドキュメントの終端が常に行頭から始まらないといけないんだけどそれだと他とインデントの深さがそろわないので空白をあとで削除してつじつまをあわせるっていうテクニックの話なのじゃないのか?

ともかく、 Gauche にはヒアドキュメントがないようなので、パス。

2009-11-30

1.8. Expanding Variables in User Input

02:35 |  1.8. Expanding Variables in User Input - a666666の日記 を含むブックマーク はてなブックマーク -  1.8. Expanding Variables in User Input - a666666の日記  1.8. Expanding Variables in User Input - a666666の日記 のブックマークコメント

http://docstore.mik.ua/orelly/perl/cookbook/ch01_09.htm

シンボリックリファレンスを使う問題。

Perl

$text =~ s/\$(\w+)/${$1}/g;

・・・これは降参。 Gauche でシンボリックリファレンスにあたるものがあるかどうか少し調べたけどわからなかった。少なくとも「シンボリックリファレンス」という用語ではそれらしいものはひっかからなかった。 Gauche のオンラインリファレンスマニュアルの目次だけ流し読みしてみたけど、ピンとくるものを見つけられなかった。 eval とかそういうこととはまた違うよなぁ・・・。ここは Perl が変態すぎる、ということでパスいち。

1.9. Controlling Case

02:56 |  1.9. Controlling Case - a666666の日記 を含むブックマーク はてなブックマーク -  1.9. Controlling Case - a666666の日記  1.9. Controlling Case - a666666の日記 のブックマークコメント

http://docstore.mik.ua/orelly/perl/cookbook/ch01_10.htm

uppercase, lowercase しろと。

Perl

use locale;                     # needed in 5.004 or above

$big = uc($little);             # "bo peep" -> "BO PEEP"
$little = lc($big);             # "JOHN"    -> "john"
$big = "\U$little";             # "bo peep" -> "BO PEEP"
$little = "\L$big";             # "JOHN"    -> "john"

$big = "\u$little";             # "bo"      -> "Bo"
$little = "\l$big";             # "BoPeep"    -> "boPeep" 

Gauche

(use srfi-13)

(string-upcase "bo peep")
(string-downcase "JOHN")

(string-titlecase "bo")
(string-titlecase "BoPeep")
;; Bopeep

うーん。文字列ライブラリsrfi-13 に string-upcase, string-downcase があるので楽勝と思いきや、それぞれ「全ての文字列を一括で全部大文字または小文字にする」のにしか使えなかった。 string-titlecase はちょっとかわってて、単語の頭を uppercase にし、それ以外の部分を lowercase にするらしい。なんか、 shinh さんがどこかでいっていた「python の title() は知りうる限り最も不要な機能」というのにちょっと近い気がするw英文を整形するときに便利だったりするのかね・・・。

string-upcase, string-downcase にはオプションで start end を渡せるのだけど、これの挙動が期待してたのと違って、結局それ単体では Perl の例と同じようにはできず。期待してたのは、「何文字目から何文字目まで大文字にする」とかだと(この問題にぴったりで)いいなぁ、という。真面目にやろうとしたらどうなるのかなぁ。文字列を分解して部分文字列に対してだけ変換をかけるようにするしかないのか。たかがこれだけのことでだいぶ面倒臭いような。 Scheme は文字列処理にはそれほど向かない、というより Perl が文字列操作を上手にやれすぎるんだな。

http://practical-scheme.net/gauche/man/gauche-refj_108.html