octahedron

LemとSKKとCommon Lispでたたかうプログラマのブログ

Seriesで響け!ユーフォニアム!

 Series触る触ると言ってたのに触ってなかったので、手始めに響けユーフォニアムをseriesでやってみました。

2017/4/29 追記

「妙なwarningが出てしまうこと」について、以下の記事の反応をいただきました。

Seriesの色々 — #:g1

この記事ではapplyでseriesをつくっているために、seriesの最適化のしくみにこの記事のコードを載せることができなかったのが原因なのでしょう。

Seriesとは

 Seriesは、Common Lispに導入されかけたけど時間がないので却下されたという、繰り返し構造を宣言的に表現するためのしくみです。Seriesを使えばHaskellClojureのような、関数型言語っぽいデータ処理ができそうな感じです。

 仕様は"Common Lisp the Language 2nd Edition"の付録Aに書かれています。英語ならウェブでも読めます。

 また日本語の説明は次のページが詳しいです。

 ちなみに使うときはこんな感じです。

CL-USER> (ql:quickload :series)
CL-USER> (use-package :series)
CL-USER> (collect 'string (subseries (apply #'series (coerce "グソクムシ" 'list)) 0 20))
"グソクムシグソクムシグソクムシグソクムシ"

響け!ユーフォニアムとは

 アニメの名前で、ぼくは見ていませんが、ちょっと前にtwitterでこんな遊びがはやっていました。

 その時期にぼくもCommon Lispformat関数でやってみたりしたので、とりあえずseriesの入門としてやってみるのにいいのではと思った次第です。

 redditlisp_jaが、似た問題でちょっと盛り上がってた時期がありました。

結果…

 Seriesのままならこんなかんじ:

CL-USER> (let* ((str "響け!ユーフォニアム")
                (len (length str)))
           (mapping ((i (scan-range :from 0 :by 1 :upto len)))
             (collect 'string
                      (subseries (apply #'series (coerce str 'list)) i (+ i len)))))
#Z("響け!ユーフォニアム" "け!ユーフォニアム響" "!ユーフォニアム響け" "ユーフォニアム響け!" "ーフォニアム響け!ユ" "フォニアム響け!ユー" "ォニアム響け!ユーフ" "ニアム響け!ユーフォ" "アム響け!ユーフォニ" "ム響け!ユーフォニア" "響け!ユーフォニアム")

 問題に沿って標準出力に出力するならこうか:

CL-USER> (let* ((str "響け!ユーフォニアム")
                (len (length str)))
           (iterate ((i (scan-range :from 0 :by 1 :upto len)))
              (format t "~a~%"
                      (collect 'string
                        (subseries (apply #'series (coerce str 'list)) i (+ i len))))))
響け!ユーフォニアム
け!ユーフォニアム響
!ユーフォニアム響け
ユーフォニアム響け!
ーフォニアム響け!ユ
フォニアム響け!ユー
ォニアム響け!ユーフ
ニアム響け!ユーフォ
アム響け!ユーフォニ
ム響け!ユーフォニア
響け!ユーフォニアム
NIL

 関数型っぽくシーケンス処理を書けるので、カッコいいと感じました。これからも使っていきたいです。

疑問

 上のコード、実行するたびに以下のようなwarningが出るんですよね。なんとかならないものか。

; in: LET* ((STR "響け!ユーフォニアム") (LEN (LENGTH STR)))
;     (ITERATE ((I (SCAN-RANGE :FROM 0 :BY 1 :UPTO LEN)))
;              (FORMAT T "~a~%"
;                      (COLLECT 'STRING
;                               (SUBSERIES (APPLY #'SERIES #) I (+ I LEN)))))
; 
; caught WARNING:
;   Warning 28 in series expression:
;   (COLLECT 'STRING (SUBSERIES (APPLY #'SERIES (COERCE STR 'LIST)) I (+ I LEN)))
;   Non-series to series data flow from:
;   (APPLY #'SERIES (COERCE STR 'LIST))
;   to:
;   (SUBSERIES (APPLY #'SERIES (COERCE STR 'LIST)) I (+ I LEN))
; 
; compilation unit finished
;   caught 1 WARNING condition