#:g1: frontpage

 

exportingの紹介

Posted 2014-04-22 17:00:00 GMT

(LISP Library 365参加エントリ)

 LISP Library 365 の113日目です。

exportingとはなにか

 exportingは、CLISPで有名なBruno Haible氏作の関数/変数定義のユーティリティです。

パッケージ情報

パッケージ名exporting
プロジェクトサイト CLOCC - Common Lisp Open Code Collection / Hg / [e6b24a] /src/ext

インストール方法

 CLOCCのプロジェクトサイトからダウンロードしてきて適当に導入します。
CLOCCは活動が停止しているものと思っていましたが、最近も更新があったりするようです。

$ hg clone http://hg.code.sf.net/p/clocc/hg clocc-hg

で全体のソースも取得できます。
src/ext/exporting/exporting.lisp が目的のソースです。

試してみる

 そもそも何をするものかというと、定義した関数/変数はエクスポートもしておきたい、というニーズを叶えるもので、定義+エクスポート、というだけのものです。
マクロ展開するとそのものズバリな内容になっています。

(exporting:defun foo (n) n)
==> (CL:PROGN (CL:EXPORT 'FOO) (CL:DEFUN FOO (N) N))
  • DEFCLASS
  • DEFCONSTANT
  • DEFGENERIC
  • DEFINE-COMPILER-MACRO
  • DEFINE-CONDITION
  • DEFINE-METHOD-COMBINATION
  • DEFINE-MODIFY-MACRO
  • DEFINE-SETF-EXPANDER
  • DEFINE-SYMBOL-MACRO
  • DEFMACRO
  • DEFMETHOD
  • DEFPARAMETER
  • DEFSETF
  • DEFSTRUCT
  • DEFTYPE
  • DEFUN
  • DEFVAR

がexportingでサポートされているフォームです。

まとめ

 今回は、exportingを紹介してみました。
SBCL等では、CL:DEFPACKAGE内でエクスポートされるシンボルも定義した場合、別個に分散してCL:EXPORTで宣言したものがあると、エラーにされたりするので、CL:DEFPACKAGE内の:EXPORTは使わない等の工夫が必要になるかと思います。〈SBCLでは、CL:DEFPACKAGEの“If the new definition is at variance with the current state of that package, the consequences are undefined.”を厳しく解釈している様子〉

 exportingに似た機能を持つものは他にも幾つかありますが、個人的には、開発時には便利なものの、パッケージ/シンボルの設計は一度決まれば、そんなに変更されるものでもないと思うので、CL:DEFPACKAGEにまとめても大した手間でもないだろう、という感じです〈SLIMEだとパッケージ定義にも飛んで行けますし…〉

chtml-matcherの紹介

Posted 2014-04-21 15:00:00 GMT

(LISP Library 365参加エントリ)

 LISP Library 365 の112日目です。

chtml-matcherとはなにか

 chtml-matcherは、Ian Eslick氏作のHTML(LHTML)に対してのパタンマッチのライブラリです。

パッケージ情報

パッケージ名chtml-matcher
Quicklisp
プロジェクトサイトeslick/chtml-matcher · GitHub
Quickdocshttp://quickdocs.org/chtml-matcher

インストール方法

(ql:quickload :chtml-matcher)

でインストールできますが、

; in: DEFUN SET-BINDING
;     (STDUTILS:ASSOC-SETF (CHTML-MATCHER::BINDINGS CHTML-MATCHER::DICT) (CHTML-MATCHER::CLEAN-VAR CHTML-MATCHER::VAR) CHTML-MATCHER::VALUE)
; --> LET* ASSOC 
; ==>
;   #<FUNCTION EQ>
; 
; caught ERROR:
;   Objects of type FUNCTION can't be dumped into fasl files.

のようなエラーになる場合、

(defmacro-exported assoc-setf (place key value &optional (test 'eq)) ...

のようにtest #'eqを 'eqに直しましょう。〈マクロ展開時に評価してしまうため、関数オブジェクトをfaslに書き出すことになってしまうのが原因〉

試してみる

 どんな関数があるかは、Quickdocsで確認できます。

 非常に大まかな説明のみで具体的な利用方法の解説がないのですが、解説によれば、"<"から始まるシンボルは、XPATHでいう//に相当するとのこと。
さらに、

  • all:複数回マッチ
  • merge:?
  • nth:位置指定〈何故か1オリジン〉
  • regex:正規表現でマッチ
  • fn:指定した関数を実行

のマッチのさせ方があります

 とりあえず、説明なしに簡単に使えそうなものから順に紹介します。

(defvar *html*
  (chtml-matcher:html->lhtml "<html><head></head><body><ul>
<li>a</li>
<li>b</li>
<li>c</li>
<li>d</li>
<li>e</li>
</ul></body></html>"))

find-in-lhtml

(chtml-matcher:find-in-lhtml *html* :ul nil)
;=>  (:UL NIL (:LI NIL "a") (:LI NIL "b") (:LI NIL "c") (:LI NIL "d") (:LI NIL "e"))

指定したノード以下を拾ってきます。

match-templateとget-bindings

(chtml-matcher:get-bindings 
 (chtml-matcher:match-template '(<ul () 
                                 (all ?elts
                                  (li () ?x)))
                               *html*))
;=>  ((:ELTS #<DICT 1 X> #<DICT 1 X> #<DICT 1 X> #<DICT 1 X> #<DICT 1 X>))

 match-templateでテンプレートとマッチさせ、get-bindingsでマッチしたものを取り出せます。
この例では、allでul以下の要素をまとめてeltsにマッチさせ、liの要素はxにマッチしています。

with-bindings

(chtml-matcher:with-bindings (elts)
                             (chtml-matcher:match-template '(<ul () 
                                                             (all ?elts
                                                              (li () ?x)))
                                                           *html*)
  (mapcan #'chtml-matcher:get-bindings elts))
;=>  ((:X . "a") (:X . "b") (:X . "c") (:X . "d") (:X . "e"))

 with-slots的にパタン変数と同名の束縛を作ります。

(chtml-matcher:with-bindings (a b c d)
                             (chtml-matcher:match-template 
                              '(<ul () 
                                (li () ?a)
                                (li () ?b)
                                (li () ?c)
                                (li () ?d))
                              *html*)
  (list a b c d))
;=>  ("a" "b" "c" "d")

まとめ

 今回は、chtml-matcherを紹介してみました。
あまりにもドキュメントがないので使い方は上記の説明で正しいのかさっぱりです。
説明がなくてもテストケースがあれば、なんとなく使い方も想像が付いたりもするのですが、テストケースもなく…。

Allegro CL: Fwrapの紹介

Posted 2014-04-20 15:00:00 GMT

(LISP Library 365参加エントリ)

 LISP Library 365 の111日目です。

Allegro CL: Fwrapとはなにか

 Allegro CL: Fwrapは、Allegro CLが提供する進化版のADVICE機構です。

パッケージ情報

パッケージ名Allegro CL: Fwrap
ドキュメントサイトFwrapping and Advice

インストール方法

 標準の状態でAllegro CLのEXCLパッケージに含まれています。

試してみる

 ADVICE機構は、恐らく最近ではAOP的な機能として考えられることが多いのではないでしょうか。
Lisp処理系には60年代後半のBBN-LISPあたりからADVICE機構が含まれていましたが、Common Lispには取り入れられなかっためか、それ程馴染みがない機能となってしまった感があります。
現在メジャーなLisp方言でサポートしているものといえば、Emacs Lispがありますが、構文的には、どうもLispマシンのADVICEと似たもののようです。
Common Lispの処理系にも、大抵Lispマシンと同様のADVICE的な機能が用意されていますが、CLOSのメソッド・コンビネーションで似たようなこともできるので、そちらを利用することが多いのかもしれません〈とはいえ微妙に使いたい場面が違いますが〉

 Allegro CL: Fwrapは、そんなADVICE機構の従来の問題点を改善したものとのこと。
使い方は単純で、

(defun bamatu (x)
  (format t "~A~%" x))


(defvar *bamatu-old* #'bamatu)


(funcall *bamatu-old* 42)
;>>  42
;>>  
;=>  NIL

のような関数を定義していたとすると、

(def-fwrapper bamatu-wrapper (x)
  (format t "before~%")
  (call-next-fwrapper)
  (format t "after~%"))

のようにラッパーを定義できます。
メソッド・コンビネーションの:aroundに似た感じで、call-next-fwrapperを呼ばないと元の関数は呼び出されません。

(fwrap 'bamatu 'w1 'bamatu-wrapper)
;=>  #<Function BAMATU>


(describe #'bamatu)
;>>  #<Function BAMATU> is a TENURED COMPILED-FUNCTION.
;>>    The arguments are (X)
;>>    It has the following indicator/fwrapper pairs, from outer to inner:
;>>  W1                #<Function BAMATU-WRAPPER>
;>>  
;=>  <no values>


(eq *bamatu-old* #'bamatu)
;=>  T

(bamatu 42)
;>>  before
;>>  42
;>>  after
;>>  
;=>  NIL


(funcall *bamatu-old* 42)
;>>  before
;>>  42
;>>  after
;>>  
;=>  NIL


(funwrap 'bamatu 'w1)
;=>  #<Function BAMATU>


(bamatu 42)
;>>  42
;>>  
;=>  NIL


(funcall *bamatu-old* 42)
;>>  42
;>>  
;=>  NIL

定義したラッパーは、fwrapで活性化させます。明示的に活性化しないと特に何も起きません。
Emacs Lispのdefadviceも定義と活性化の二段階に分けることができるので似ているかもしれません。
ラッパーを付けた前後で関数がeqなことが改善点の一つとのこと。

マクロにラップ

 また、マクロにもラップ可能です。

(defmacro begin (&body body)
  `(progn . ,body))


(def-fwrapper begin-wrapper (&rest args)
  (pprint args)
  (call-next-fwrapper))


(fwrap 'begin :pp 'begin-wrapper)


(begin 1 2 3)
;>>  
;>>  ((BEGIN 1 2 3) NIL)
;=>  3


(macrolet ((foo (n) n))
  (begin (foo 8)))
;>>  
;>>  ((BEGIN (FOO 8)) #<Augmentable INTERPRETER environment 1>)
;=>  8

マクロの環境にもアクセスできるようになっています。

ローカル関数にもラップ

 Allegro CL: Fwrapで凄いのが、ローカル関数もラップできるところ

(defun calfo (n)
  (flet ((masopic (n)
           (* 100 n)))
    (masopic n)))


(def-fwrapper masopic-wrapper (n)
  (let ((ans (call-next-fwrapper)))
    (format t "(masopic ~A) => ~A" n ans)
    ans))


(fwrap '(flet calfo masopic) :w1 'masopic-wrapper)


(calfo 42)
;>>  (masopic 42) => 4200
;=>  4200


(defun calfo (n)
  (flet ((masopic (n)
           (* 1000 n)))
    (masopic n)))


(calfo 42)
;>>  (masopic 42) => 42000
;=>  42000

再定義した場合でもラッパーは残るので、変更は別途fwrap等で行うことになります。

関連

まとめ

 今回は、Allegro CL: Fwrapを紹介してみました。
久し振りに1972年のBBN-LISPのマニュアルのADVICEの説明を読んでみましたが、典型的なADVICEの利用場面として、TRACE、BREAK、BREAKDOWN〈個別の関数の時間の測定〉が挙げられていました。
TRACEは、現在のCommon Lisp処理系でも内部でADVICE機構を利用していることが多いかと思いますが、ラッパーでBREAKやTIMEを仕込むのも使い方によってはデバッグ時に便利かもしれないですね。

3bmdの紹介

Posted 2014-04-20 10:00:00 GMT

(LISP Library 365参加エントリ)

 LISP Library 365 の110日目です。

3bmdとはなにか

 3bmdは、3b氏作のCommon Lispでmarkdown形式を処理するためのライブラリです。

パッケージ情報

パッケージ名3bmd
Quicklisp
CLiKihttp://cliki.net/3bmd
Quickdocshttp://quickdocs.org/3bmd

インストール方法

(ql:quickload :3bmd)

試してみる

 どんな関数があるかは、Quickdocsで確認できます。

 前回は、cl-markdownを紹介してみましたが、3bmdも大体同じようなAPIになっています。
parse-string-and-print-to-streamでmarkdownの文字列を変換してストリームに出力、3bmd:parse-and-print-to-streamでファイルを変換してストリームに出力します。

(3bmd:parse-string-and-print-to-stream 
 "## 挨拶
おはよう日本"
 *standard-output*)
;>>  
;>>  
;>>  <h2>挨拶</h2>
;>>  
;>>  <p>おはよう日本</p>
;>>  
;=>  NIL

(3bmd:parse-and-print-to-stream "3bmd-20131213-git/README.md"
                                *standard-output*)
;>> <p>Common Lisp <a href="http://daringfireball.net/projects/markdown/" >Markdown</a> -&gt; html converter, using <a href="https://github.com/nikodemus/esrap" >esrap</a> for parsing, and grammar based on <a href="https://github.com/jgm/peg-markdown" >peg-markdown</a>.</p>
;>> ....

 Lisp星人的に嬉しい機能としてcolorizeとの連携があります。

(ql:quickload :3bmd-ext-code-blocks)

として拡張機能をロードすれば、3bmd-code-blocks:*code-blocks* => Tとすることでcolorizeで処理が可能です。

(let ((3bmd-code-blocks:*code-blocks* t)
      (3bmd:*smart-quotes* t))
  (3bmd:parse-string-and-print-to-stream 
  "
```lisp
 (+ 3 3)
;=> 6
```
"
  *standard-output*))
;>>  
;>>  
;>>  <pre><code><span class="code"> <span class="paren1">(<span class="code"><a href="http://www.lispworks.com/reference/HyperSpec/Body/a_pl.htm" class="symbol">+</a> 3 3</span>)</span>
;>>  <span class="comment">;=&gt; 6</span></span></code></pre>
;>>  
;=>  NIL

 ちなみに、実際の利用では、CSSの指定をすることになるかと思います

(let ((3bmd-code-blocks:*code-blocks* t))
  (who:with-html-output (out out :prologue T :indent T)
    (:style #.colorize:*coloring-css*) 
    (3bmd:parse-and-print-to-stream 
     "/var/tmp/foo.md"
     *standard-output*)))

等々

関連

まとめ

 今回は、3bmdを紹介してみました。
colorizeと連携するのが簡単なようなので、S式で書いているこのブログもmarkdown形式に移行してみたいところです。
しかし、3bmdという名前ではmarkdownのライブラリであることに気付くのは難しいですね〈quicksearchでは見付けられますが…〉。

cl-markdownの紹介

Posted 2014-04-19 05:00:00 GMT

(LISP Library 365参加エントリ)

 LISP Library 365 の109日目です。

cl-markdownとはなにか

 cl-markdownは、Gary W. King氏作のCommon Lispでmarkdown形式を扱うためのライブラリです。

パッケージ情報

パッケージ名cl-markdown
Quicklisp
参考サイト
CLiKihttp://cliki.net/cl-markdown
Quickdocshttp://quickdocs.org/cl-markdown

インストール方法

(ql:quickload :cl-markdown)

試してみる

 どんな関数があるかは、Quickdocsで確認できます。

 使い方はシンプルで、markdown:markdownに変換したい文字列/ストリーム/パスオブジェクトを渡すと指定したストリームにHTMLの文字列が出力されます。

(markdown:markdown "
# cl-markdown の使い方

markdownで書いてhtmlの出力を期待している"
                   :stream t)
;>>  <h1>cl-markdown の使い方</h1><p>markdownで書いてhtmlの出力を期待している </p>
;=>  #<MARKDOWN::DOCUMENT  # cl-markdown の使い方  markdownで書いてhtmlの出力を期待している {101FD616C3}>
;    NIL

 複数のファイル/文字列を変換するmarkdown:markdown-manyというものも用意されています。
どうも:formatに明示的に形式を渡さないとエラーになるようです〈バグ?〉。

;>>  Parsing: "こんにちは"
;>>  Rendering: "/tmp/k.html"
;=>  #<MARKDOWN::MULTI-DOCUMENT 1 children {101C709603}>
;    ((#<MARKDOWN::CHILD-DOCUMENT こんにちは {101C70A0F3}> "/tmp/k.html"))

 また、markdown:defextension、markdown:defsimple-extensionで記法を拡張することも可能です。詳細はドキュメントとサンプルコードを参照してみましょう。
ちょっと試してみた感想ですが、拡張したいことがシンプルなら、そこそこ簡単に拡張できるのではないでしょうか。

まとめ

 今回は、cl-markdownを紹介してみました。
世間で使われているmarkdownは割合に拡張されていたりするので、素のmarkdownだと寂しく感じることがありますね。

cl-rssの紹介

Posted 2014-04-17 16:00:00 GMT

(LISP Library 365参加エントリ)

 LISP Library 365 の108日目です。

cl-rssとはなにか

 cl-rssは、Kevin M. Rosenberg氏作のRSS 2.0をパーズしてCLOSのオブジェクトにしてくれるライブラリです。

パッケージ情報

パッケージ名cl-rss
Quicklisp
参考サイト
CLiKihttp://cliki.net/cl-rss
Quickdocshttp://quickdocs.org/cl-rss

インストール方法

(ql:quickload :rss)

試してみる

 どんな関数があるかは、Quickdocsで確認できます。

 ライブラリにはサンプル的にrss:show-sitesが定義されているので、この辺りをいじってみると使い方が分かります。
rss:show-sitesだと、タイトルのみなので、リンクのURLも表示するように改造してみます。
rss:rss-itemは

(*:slot-names (find-class 'rss-item))
;=>  (TITLE LINK DESCRIPTION PUB-DATE)

のような感じになっています。

(defun show-sites/ (&optional (sites *sites*))
  (dolist (site (mklist sites))
    (awhen (rss-site site)
           (display-site/ it))))


(defun display-site/ (site &key (stream *standard-output*))
  (format stream "Site: ~A~%" (title site))
  (dolist (item (items site))
    (format stream "  ~A~%~4T~A~2%" (title item) (link item))))


(show-sites/ '("http://www.reddit.com/r/lisp/.rss"))
;>>  Site: Lisp
;>>    Fear of Macros
;>>      http://www.reddit.com/r/lisp/comments/239ee3/fear_of_macros/
;>>  
;>>    Dylan 2016: Evolving Dylan
;>>      http://www.reddit.com/r/lisp/comments/239r8i/dylan_2016_evolving_dylan/
;>>  
;>>    {} descriptions is a meta level descriptions library for Common Lisp.
;>>      http://www.reddit.com/r/lisp/comments/238zj2/descriptions_is_a_meta_level_descriptions_library/
;>>  
;>>    Noobtuts - Two Clojure Math Tutorials
;>>      http://www.reddit.com/r/lisp/comments/239j9o/noobtuts_two_clojure_math_tutorials/
;>>  
;>>    The Fate of "The ANSI Common Lisp Reference Book"
;>>      http://www.reddit.com/r/lisp/comments/236xta/the_fate_of_the_ansi_common_lisp_reference_book/
;>>  
;>>    Common Lisp implemented in Emacs Lisp
;>>      http://www.reddit.com/r/lisp/comments/235uii/common_lisp_implemented_in_emacs_lisp/
;>>  
;>>    Babel2
;>>      http://www.reddit.com/r/lisp/comments/233mkx/babel2/
;>>  
;>>    The Paren Programming Language now supports threads.
;>>      http://www.reddit.com/r/lisp/comments/233d3t/the_paren_programming_language_now_supports/
;>>  
;>>    Nullable objects in Common Lisp?
;>>      http://www.reddit.com/r/lisp/comments/22xn03/nullable_objects_in_common_lisp/
;>>  
;>>    OSX GUI tutorial
;>>      http://www.reddit.com/r/lisp/comments/22wwko/osx_gui_tutorial/
;>>  
;>>    Strange sbcl/cffi behaviour
;>>      http://www.reddit.com/r/lisp/comments/22wywo/strange_sbclcffi_behaviour/
;>>  
;>>    Puppet Labs move to Clojure
;>>      http://www.reddit.com/r/lisp/comments/22wdct/puppet_labs_move_to_clojure/
;>>  
;>>    Meagan Waller - Conditional binding in Clojure
;>>      http://www.reddit.com/r/lisp/comments/22ts2k/meagan_waller_conditional_binding_in_clojure/
;>>  
;>>    Interview with Stuart Halloway, author of 'Programming Clojure'
;>>      http://www.reddit.com/r/lisp/comments/22om7e/interview_with_stuart_halloway_author_of/
;>>  
;>>    Whatever became of CLIM?
;>>      http://www.reddit.com/r/lisp/comments/22lbpe/whatever_became_of_clim/
;>>  
;>>    Lisp interface library: video presentation
;>>      http://www.reddit.com/r/lisp/comments/22i9v7/lisp_interface_library_video_presentation/
;>>  
;>>    Clojure Cookbook has arrived
;>>      http://www.reddit.com/r/lisp/comments/22a1q3/clojure_cookbook_has_arrived/
;>>  
;>>    Cache friendly memory management?
;>>      http://www.reddit.com/r/lisp/comments/2283i0/cache_friendly_memory_management/
;>>  
;>>    Octopus
;>>      http://www.reddit.com/r/lisp/comments/22710w/octopus/
;>>  
;>>    Lisp Aha! moments
;>>      http://www.reddit.com/r/lisp/comments/2235wt/lisp_aha_moments/
;>>  
;>>    SLIME User Manual version 2.5 (PDF)
;>>      http://www.reddit.com/r/lisp/comments/2235zh/slime_user_manual_version_25_pdf/
;>>  
;>>    SBCL Release Versions
;>>      http://www.reddit.com/r/lisp/comments/2242jf/sbcl_release_versions/
;>>  
;>>    Hashtable variant of (describe)
;>>      http://www.reddit.com/r/lisp/comments/224oo2/hashtable_variant_of_describe/
;>>  
;>>    Real World Clojure
;>>      http://www.reddit.com/r/lisp/comments/222pl9/real_world_clojure/
;>>  
;>>    Wasn't there something about indentation tuning with Slime recently?
;>>      http://www.reddit.com/r/lisp/comments/220ehs/wasnt_there_something_about_indentation_tuning/
;>>  
;>>  
;=>  NIL

 ちなみに、parse-rss-streamの中のparse-channel-childがコケてrss-parse-errorを上げますが、今回はCL:IGNORE-ERRORSで囲んでスルーします。

(defun parse-rss-stream
  ...
  (map nil (lambda (child) 
             (ignore-errors
               (parse-channel-child child rss strict?)))
       (xmls:xmlrep-children channel))
  ...)

また、処理系によって、net.aserve.client:do-http-requestの文字化けが良く分からないという場合は、drakma:http-requestに差し替えるのも手かもしれません。

まとめ

 今回は、cl-rssを紹介してみました。
ライブラリを修正するより自作した方が早いという人もいるかもしれません。

sloopの紹介

Posted 2014-04-16 15:00:00 GMT

(LISP Library 365参加エントリ)

 LISP Library 365 の107日目です。

sloopとはなにか

 sloopは、William Schelter氏作が1985年頃に作製した繰り返しマクロのライブラリです。
基本的な機能はMITのLOOP互換になっています。

パッケージ情報

パッケージ名sloop
参考サイトPackage: lang/lisp/code/iter/loop/sloop/

インストール方法

 上記CMUのAIレポジトリからソースをダウンロードして動かしてみましょう。
AIレポジトリのソースは改変禁止と書いてありますが、GCLのソースはGPLなので改変して再配布する際にはGCLのものが良いかと思います〈GCL版は若干の修正がされています〉

試してみる

 1984年の最初のCommon Lispでは、LOOPは単純な無限ループの機能のみとなっていました。
現在ANSI Common Lispに含まれているLOOPの源流のMIT系LispマシンのLOOPは、1980年位からLispマシン上で利用されていて、一定の支持は集めていたようですが、Common Lispにそのまま導入するかどうかはCLtL1の段階では見送られたというところでした。

 CLtL1にリッチな繰り返し構文が含まれなかったお蔭で1980年代後半に結局Lispマシン系のLOOPがCommon Lispに採用されるまでの間に、色々な繰り返し構文や、LOOP互換構文が登場しました。
sloopもそんなものの一つで、CLtL1なKCLやAKCLで専ら利用されたようです。
William Schelter氏は、AKCLの開発者だったようで、AKCL〜GCLでも結構な量のコードがsloopを利用して記述されていたりします。

 基本的な機能はANSI Common LispのLOOPと同様です。

(let ((lis '(1 3 4 2 1 6 7 3 0 22)))
  (sloop :for (i a) :on lis :by 'cddr
         :when (oddp i)
         :collecting i
         :else
         :append (sloop :for j :below a :collecting j)))
;=>  (1 0 1 1 7 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21)


(let ((n 5))
  (sloop :for i :below n
	 :for a :below (* 2 n)
         :unless (oddp i)
         :sum i
         :else
         :sum (sloop :for j :below a :sum j)))
;=>  9

(let ((lis '(1 3 4 2 1 6 7 3 0 22)))
  (sloop :for (i a) :on lis :by 'cddr
         :when (oddp i)
         :sum i
         :and :count i
         :else
         :sum
         (sloop :for j :below a :sum j)))
;=>  244


(let ((lis '(1 3 4 2 1 6 7 3 0 22)))
  (sloop for (a i) on lis by 'cddr
         when (not (zerop a))
         nconc (list (* 2 a) i)
         else nconc (list 3 i)
         while (not (eql i 6))))
;=>  (2 3 8 2 2 6)


(let ((lis '(1 3 4 2 1 6 7 3 0 22)))
  (sloop for (a i) on lis by 'cddr
         when (not (zerop a))
         nconc (list (* 2 a) i) into tem
         else nconc (list 3 i) into tel
         finally (loop-return (append tem tel))))
;=>  (2 3 8 2 2 6 14 3 3 22)

 以上のように基本的なところは同じですが、パッケージや、ハッシュテーブル等の利用では少し書き方が違ってきます。

(let ((table (make-hash-table)))
  (setf (gethash 3 table) 11)
  (setf (gethash 11 table) 7)
  (sloop for (key elt) in-table table sum elt into tot sum key into keys
	 finally (loop-return (list keys tot))))
;=>  (14 18)


(sloop :for sym in-package :cl
       :when (eql sym 'cons) :do (loop-return sym))
;=>  CONS

in-packageや、in-tableで指定します。ANSI CLのLOOPより分かりやすい気もしないでもありません。

(sloop :for i :from 0 :to 99
       :declare (fixnum i)
       :sum i)
;=>  4950

さらに型宣言はdeclareキーワードで指定します。

 加えて独自の拡張機能があります。

sloop
(sloop :for i :below 5
       sloop (for j  :to i :collecting (list i j)))
;=>  ((0 0) (1 0) (1 1) (2 0) (2 1) (2 2) (3 0) (3 1) (3 2) (3 3) (4 0) (4 1) (4 2) (4 3) (4 4))

入れ子にする際に(sloop ... (sloop forではなく、(sloop ... sloop (forと書けるようにするもの。すっきりするようなしないような。

in-carefully
(sloop for e in-carefully '(1 2 3 . 4) collect e)
;=>  (1 2 3)

NIL終端でないリストは終端を無視して扱うという指定

in-fringe
(sloop :for x in-fringe '(1 2 (3 (4 5) . *) 8 1 2)
       :collect x)
;=>  (1 2 3 4 5 * 8 1 2)

リストをflattenしてくれるもの

averaging
(sloop :for e :from 1 :to 99 :averaging e)
;=>  50.0

平均を算出

collate
(sloop for v in-fringe '(1  (7 3 (2 . 2) 9 (4 11)) 5 . 3)
       when (oddp v)
       collate v)
;=>  (1 3 5 7 9 11)
(sloop repeat 15 collate (random 2))
;=>  (0 1)

昇順にソートしつつ重複も削除するcollect

 ユーザーも定義することが可能でsloop:def-loop-collect、sloop:def-loop-map、 sloop:def-loop-forあたりで定義が可能です。上記の拡張キーワードはこれらで定義されているので独自に定義する場合には参考になると思います。

まとめ

 今回は、sloopを紹介してみました。
GCLのソースを眺めていて、SLOOPという見慣れない物が大量に使われているのを発見したので紹介してみました。

eggs: progress-indicatorsの紹介

Posted 2014-04-15 15:00:00 GMT

(LISP Library 365参加エントリ)

 LISP Library 365 の106日目です。

eggs: progress-indicatorsとはなにか

 eggs: progress-indicatorsは、Chickenのターミナル上にプログレスバーを表示するライブラリです。

パッケージ情報

パッケージ名eggs: progress-indicators
ドキュメントprogress-indicators - The Chicken Scheme wiki

インストール方法

 chicken-installを利用して

sudo chicken-install progress-indicators

とでもしてあれば、

(use progress-indicators)

で利用できます。

 どんな関数があるかは、ドキュメントサイトから確認できますが、プログレスバーを表示する関数と、くるくる回転するスピナーを表示する関数の2つが主です。
Gaucheのtext.progressでは、表示用のクロージャーを生成する方式でしたが、eggs: progress-indicatorsでは、プログレスバーのオブジェクト〈構造体〉を表示用の関数が表示する、という作りになっています。

(use progress-indicators)
(use srfi-42)
(use srfi-18)


(let ((bar (make-progress-bar #:max 256)))
  (do-ec (: i 256)
    (begin
      (show-progress-bar bar)
      (advance-progress-bar! bar)
      (thread-sleep! 0.1)))
  (newline))
;>> [256/256=====================================================>|  100%]
(let ((bar (make-progress-bar #:max 256 #:end-message "完")))
  (do-ec (: i 256)
    (begin
      (show-progress-bar bar)
      (advance-progress-bar! bar)
      (thread-sleep! 0.1)))
  (finish-progress-bar! bar)
  (newline))
;>> [256/256======================================================|完]

finish-progress-bar!を利用すると完了時のメッセージを表示させ終了させます。

 スピナーの方も、


(let ((s (make-spinner)))
  (do-ec (: i 25600)
    (begin
     (advance-spinner! s)
     (thread-sleep! 0.1)))
  (finish-spinner! s)
  (newline))
;>> / 

のようにして利用できます。
また、プログレスバーと組み合わせての利用も可能です。

(let ((s (make-spinner))
      (bar (make-progress-bar #:max 256 #:end-message "完")))
  (do-ec (: i 256)
    (begin
      (show-progress-bar bar)
      (advance-progress-bar! bar)
      (advance-spinner! s)
      (thread-sleep! 0.1)))
  (finish-progress-bar! bar)
  (finish-spinner! s)
  (newline))
;>> [[256/256======================================================|完]100%/

まとめ

 今回は、eggs: progress-indicatorsを紹介してみました。
シェルスクリプトで利用されることが少ない為か、Common Lispではこういうのが少ない気がします。Common Lispでもこういうのがあると良いですね。

text.progressの紹介

Posted 2014-04-14 15:00:00 GMT

(LISP Library 365参加エントリ)

 LISP Library 365 の105日目です。

text.progressとはなにか

 text.progressは、Gaucheでコンソールにプログレスバーを表示するためのライブラリです。

パッケージ情報

パッケージ名text.progress
GaucheマニュアルGauche ユーザリファレンス: 11.43 text.progress - テキスト端末上でプログレスを表示する

インストール方法

(use text.progress)

試してみる

 詳しくドキュメントが書かれているので眺めるだけで使い方は分かると思いますが、(make-text-progress-bar)が関数を生成するようになっていて、生成された関数を実行することでプログレスバーが描画されます。

(let ((bar (make-text-progress-bar)))
  (dotimes (i 100)
    (sys-nanosleep 10000000)
    (bar 'inc 1))
  (newline))

;>> progress      |########################################|  100/100  00:00 ETA
;=> #<undef>

描画の仕組みですが、バーを描画して復帰してまた描画して、の繰り返しで、

(begin 
  (dotimes (i 100)
    (sys-nanosleep 100000000)
    (display (make-string i #\#))
    (display (make-string (- 100 i) #space))
    (display ": ")
    (display i)
    (display "\r")
    (flush))
  (newline))

;>> ################################################################################################### : 99
;=> #<undef> 

のようなものが骨子です。

 バーの生成については、オプションが色々と取れるようになっていて、カスタマイズが簡単にできるようになっています。

(let ((bar (make-text-progress-bar :header "試験"
                                   :header-width 2
                                   :bar-char #\o
                                   :num-width 13
                                   :max-value 256)))
  (dotimes (i 256)
    (sys-nanosleep 10000000)
    (bar 'inc 1))
  (newline))

;>> 試験|oooooooooooooooooooooooooooooooooooooooo|      256/256  00:00 ETA
;=> #<undef>

まとめ

 今回は、text.progressを紹介してみました。
ちょっとしたスクリプトの作成でこういうのが欲しいことがありますが、手軽に利用できて良いですね。

cl-string-matchの紹介

Posted 2014-04-13 15:00:00 GMT

(LISP Library 365参加エントリ)

 LISP Library 365 の104日目です。

cl-string-matchとはなにか

 cl-string-matchは、Victor Anyakin氏作の文字列探索のライブラリです。

パッケージ情報

パッケージ名cl-string-match
Quicklisp
プロジェクトサイトcl-string-match | Free Development software downloads at SourceForge.net
CLiKihttp://cliki.net/cl-string-match
Quickdocshttp://quickdocs.org/cl-string-match

インストール方法

(ql:quickload :cl-string-match)

試してみる

 どんな関数があるかは、Quickdocsで確認できます。

 現在のところ

  • Brute-force
  • ボイヤー-ムーア法
  • Boyer-Moore-Horspool
  • ラビン-カープ法
  • クヌース-モリス-プラット法
  • エイホ-コラシック法
  • Ukkonen's suffix tree construction

がサポートされているようです。

(defvar *text* (*:read-file-to-string "/usr/share/dict/words"))


(search "lithographing" *text*)
;=>  548599


(sm:string-contains-ac "lithographing" *text*)
;=>  548599


(sm:string-contains-bm "lithographing" *text*)
;=>  548599


(sm:string-contains-rk "lithographing" *text*)
;=>  NIL


(sm:string-contains-bmh "lithographing" *text*)
;=>  NIL


(sm:string-contains-kmp "lithographing" *text*)
;=>  548599


(sm:string-contains-brute "lithographing" *text*)
;=>  548599




(let ((idx (sm:initialize-rk "list")))
  (sm:search-rk idx *text*))
;=>  NIL


(let ((idx (sm:initialize-ac "list")))
  (sm:search-ac idx *text*))
;=>  3365
;    0


(let ((idx (sm:initialize-bm "list")))
  (sm:search-bm idx *text*))
;=>  3365


(let ((idx (sm:initialize-bmh "list")))
  (sm:search-bmh idx *text*))
;=>  21736


(let ((idx (sm:initialize-kmp "list")))
  (sm:search-kmp idx *text*))
;=>  3365

bmhとrkの結果がどういうことなのかについては置いておいて、速度等はどうなのかCL:SEARCHとでも比較してみます〈bmhとrkは除外〉

(defmacro ss-bench (pat text &optional (times 100))
  `(progn 
     ,@(mapcar (lambda (x)
                 (let ((xpr `(time 
                              (dotimes (i ,times)
                                (,x ,pat ,text)))))
                   `(progn
                      (pprint ',xpr)
                      ,xpr)))
               '(search 
                 sm:string-contains-ac
                 sm:string-contains-bm
                 ;; sm:string-contains-rk
                 ;; sm:string-contains-bmh
                 sm:string-contains-kmp
                 sm:string-contains-brute))))
(ss-bench "lithographing" *text*)
;>> (TIME (DOTIMES (I 100) (SEARCH "lithographing" *TEXT*)))
;>> Evaluation took:
;>>   1.546 seconds of real time
;>>   1.544096 seconds of total run time (1.544096 user, 0.000000 system)
;>>   99.87% CPU
;>>   3,700,604,277 processor cycles
;>>   0 bytes consed
;>>   
;>> 
;>> (TIME (DOTIMES (I 100) (SM:STRING-CONTAINS-AC "lithographing" *TEXT*)))
;>> Evaluation took:
;>>   4.300 seconds of real time
;>>   4.272267 seconds of total run time (4.272267 user, 0.000000 system)
;>>   99.35% CPU
;>>   10,293,248,214 processor cycles
;>>   1,694,576 bytes consed
;>>   
;>> 
;>> (TIME (DOTIMES (I 100) (SM:STRING-CONTAINS-BM "lithographing" *TEXT*)))
;>> Evaluation took:
;>>   0.303 seconds of real time
;>>   0.304019 seconds of total run time (0.304019 user, 0.000000 system)
;>>   100.33% CPU
;>>   725,790,519 processor cycles
;>>   221,072 bytes consed
;>>   
;>> 
;>> (TIME (DOTIMES (I 100) (SM:STRING-CONTAINS-KMP "lithographing" *TEXT*)))
;>> Evaluation took:
;>>   0.467 seconds of real time
;>>   0.468029 seconds of total run time (0.468029 user, 0.000000 system)
;>>   100.21% CPU
;>>   1,118,114,415 processor cycles
;>>   32,704 bytes consed
;>>   
;>> 
;>> (TIME (DOTIMES (I 100) (SM:STRING-CONTAINS-BRUTE "lithographing" *TEXT*)))
;>> Evaluation took:
;>>   0.389 seconds of real time
;>>   0.388024 seconds of total run time (0.388024 user, 0.000000 system)
;>>   99.74% CPU
;>>   929,401,578 processor cycles
;>>   38,784 bytes consed

bm(ボイヤー-ムーア法)あたりは5倍程度速いようです。

まとめ

 今回は、cl-string-matchを紹介してみました。
プロジェクトページもドキュメントも綺麗にまとまっているので今後の発展に期待したいプロジェクトです。

Older entries (1626 remaining)