読者です 読者をやめる 読者になる 読者になる

逆FizzBuzz

CommonLisp

http://www.jasq.org/2/post/2012/05/inverse-fizzbuzz.html
http://d.hatena.ne.jp/matarillo/20120515/p1
より。

入力を正規表現に変換してしまえばとても簡単になる。
https://gist.github.com/2708823

けれど、cl-ppcreだと上記のコードのままじゃちょっと長めの入力を渡すと死んでしまう。
バックトラックが爆発してるんだろうけど、正しい書き方あるのかな。

CL-USER(10): (defun test (n) (time (inverse-fizzbuzz (loop repeat n append '(fizz buzz fizz fizz buzz fizz fizzbuzz)))))

TEST
CL-USER(11): (test 5)

Evaluation took:
  0.091 seconds of real time
  0.093601 seconds of total run time (0.093601 user, 0.000000 system)
  103.30% CPU
  225,231,803 processor cycles
  65,520 bytes consed
  
(3 75)
CL-USER(12): (test 6)

Evaluation took:
  8.923 seconds of real time
  8.907657 seconds of total run time (8.907657 user, 0.000000 system)
  99.83% CPU
  22,234,078,381 processor cycles
  98,296 bytes consed
  
(3 90)
CL-USER(13): (test 7)
;; 返って来ない…

7個以上渡された時は最初の7個の繰り返しになってるか確認してから最初の7個をチェック、で終点は入力の長さから計算って形にしないといけなさそう。

ちなみにxyzzy正規表現検索だと全く問題なし。

user> (defun scan-all (re str &optional (start 0))
	(let ((s (string-match re str start)))
	  (when s (cons (list (1+ s) (match-end 0)) (scan-all re str (1+ s))))))

scan-all
user> (defun inverse-fizzbuzz (fb)
	(let ((re (format nil "~{~A~^_*?~}" (mapcar #'(lambda (s) (case s (fizz "f") (buzz "b") (t "z"))) fb)))
	      (pattern (format nil "~V@{__f_bf__fb_f__z~}" (+ 2 (floor (length fb) 7)) t)))
	  (car (sort (scan-all re pattern) #'> :key (lambda (p) (apply #'- p))))))
inverse-fizzbuzz
user> (defun test (n)
	(inverse-fizzbuzz
	 (loop repeat n append '(fizz buzz fizz fizz buzz fizz fizzbuzz))))

test
user> :time (test 5)
(3 75)
----------
0.000 sec.
user> :time (test 10)
(3 150)
----------
0.000 sec.
user> :time (test 1000)
(3 15000)
----------
0.484 sec.

7000個渡しても大丈夫。

(宣伝) WWO第5回定期演奏会

winds

明後日26日に長野県千曲市のあんずホールにて吹奏楽の演奏会があります。
http://dl.dropbox.com/u/215714/willow/2012concert/wwo-5th-concert.png
1段目のはじっこの方で吹いてます。

曲目

北信にお住まいの方、90年代課題曲マニアの方は是非お越しくださいませ。
入場料は\500です。(未就学児無料)

lisp-repl-mode for xyzzy

xyzzy Lisp

Lisp処理系には付き物のREPLですが、xyzzy lisp用の物はありそうで見つからなかったので作りました。

標準の*scratch*では面倒だった*package*の切り替えが簡単になったり、色々捗ります。
http://gyazo.com/457ac8f8b6067f151ccdefb0a7cdad72.png

標準の*scratch*に比べて良い点

  • カレントパッケージを切り替えられる
    • *buffer-package*の更新も行うので、ac-modeやldocを導入している場合色々便利に
  • REPL変数が使える
  • 便利なREPLコマンド (自作可)

注意点

rx.lを導入している場合、rx.l内で使用されているシンボル'ed::**と
xl-repl.l中でlispパッケージよりexportしようとしている'lisp:**がコンフリクトします。
rx.lより先(ni/setupより先)にxl-replをロードするか、siteinit.lの先頭に

(export '(lisp::** lisp::*** lisp::++ lisp::+++ lisp::// lisp::///) :lisp)

というコードを書くなどして回避してください。

インストール

NetInstallerにhttp://youz.github.com/xyzzy/package.lを登録して*scratch*よりインストールし、.xyzzy (or siteinit.l) に

(require "xl-repl")

と追記してください。

設定

ac-modeやldoc2を導入している場合、以下の設定を.xyzzyに加えておくと便利です。

; ldoc2 を使っている場合
(push 'ed:lisp-repl-mode ed::*ldoc-activated-mode-list*)
; ac-mode を使っている場合
(push 'ed:lisp-repl-mode ed::*ac-mode-lisp-mode*)

REPLバッファ上で、パッケージ修飾子無しでもカレントパッケージからシンボル名補完ができたり、ldoc2での説明表示ができるようになります。

起動

M-x start-repl

キーマップ

REPLバッファ用キーマップ repl:*keymap* は、 ed:*lisp-mode-map* をベースにして以下のキーバインドを追加/上書きしています。

  • RET -- repl::newline-or-eval-input (入力した式を評価 / 書きかけならlisp-newline-and-indent)
  • C-l -- repl::clear-repl (REPLバッファをクリア)
  • M-C-a -- repl::beginning-of-input (入力中の式の先頭へ移動)
  • M-C-e -- repl::end-of-input (入力中の式の最後へ移動)
  • C-p -- repl::previous-input (入力履歴 - 戻る)
  • C-n -- repl::next-input (入力履歴 - 進む)

REPLコマンド

以下のキーワードシンボル(と引数)を入力するとREPLコマンドを実行します。

:cd (&optional dir)
    ; default-directoryをdirへ移動 (dir省略時はdefault-directoryを表示)
:describe (symbol-or-package-name)
    ; パッケージ/変数/定数/関数の説明を表示
:dir (&optional wildcard)
    ; default-directoryのファイルを列挙
:expand (form)
    ; formをmacroexpandして表示
:help (&optional pattern)
    ; REPLコマンドの説明を表示
:load (name)
    ; *load-path*にdefault-directoryを含めて(load-library 'name)を評価
:ls (&optional pat (pkg *package*))
    ; パッケージ内の変数/定数/関数シンボルを列挙
:lsall (&optional pattern)
    ; 全パッケージの変数/定数/関数シンボルを列挙
:lsext (&optional pattern (pkg *package*))
    ; パッケージよりexportされている変数/定数/関数シンボルを列挙
:lspkg (&optional pattern)
    ; パッケージ名を列挙
:mkpkg (name &rest options)
    ; (make-package 'name [options])を評価し、*package*を作成したパッケージに変更
:package (name)
    ; (in-package 'name)を評価
:require (name)
    ; *load-path*にdefault-directoryを含めて(require 'name)を評価
:time (form)
    ; formを評価し、実行時間(秒)を表示

適当に省略してもOK。

user> :pa repl.command     ; :packageコマンド
#<package: repl.command>

repl.command> :de package  ; :describeコマンド
<Function> repl.command::package (name)
    ; (in-package 'name)を評価

:cd コマンドは今の所引数に文字列しか受け付けないので注意

user> :cd site-lisp
不正なデータ型です: site-lisp: pathname

user> :cd "site-lisp"
c:/usr/xyzzy/site-lisp

user> :cd "C:/Users/yz"
C:/Users/yz

他のコマンドはシンボルを渡してもOK。

user> :dir *x*
[C:/Users/yz/]
.VirtualBox/
Dropbox/
lynx.rc
lynx_bookmarks.htm
VirtualBox VMs/

REPLコマンドの自作

パッケージrepl.command内に関数を定義すると、その関数がREPLコマンドとして使えるようになります。
定義例はソースを見てください。
repl.commandパッケージはlispパッケージをuseしていないので、(in-package :repl.command)と書いてから追加コマンドを定義するのは色々面倒です。
lispやeditorをuseしている適当なパッケージから(defun repl.command::hoge () ...)と書く方が良いでしょう。
(repl.commandパッケージがlispパッケージをuseしていないのは、loadやrequireといったlispパッケージにあるような名前のコマンドを簡単に作成したかった為です)

不具合等

見つかりましたら githubのissues@Yubeshiまで。

投稿モードバッファ (xyttr Advent Calendar 25日目分)

xyzzy xyttr

3日遅れでxyttr Advent Calendar 2011の最終記事です。

xyttrはあまりツイートしない人間が作ってる事もあって投稿機能に力が入ってないのですが、もうちょっと何かあった方が良いよなーという事で今後のために投稿用バッファのプロトタイプを作ってみました。
hatena-haiku-modeの投稿画面を参考にしています。

https://gist.github.com/1522725

このファイルをロードすると投稿系のキーバインド(u, @, S-@, r u, r r)が下図のような投稿用バッファを開くコマンドに変更されます。

http://gyazo.com/885210cbda66ae1d9d67c0c41e60b635.png

C-c C-cで罫線以下のテキストが投稿されます。
今の所特殊な編集機能はありませんが、ミニバッファでの操作制限(undo等)がなくなっただけでも結構便利になったかと思います。というか今までが不自由すぎたか…

年末・年始で色々試して、次のリリースまでに本体へ組み込みたいと思います。

xyttrで画像投稿 (xyttr Advent Calendar 24日目分)

xyzzy xyttr

完全に遅刻ですがxyttr Advent Calendar 2011の記事です。

xyttrでやる意味あるの? というと利便性上ほとんど意味ないですが、出来てしまった物はしかたがない。

https://gist.github.com/1521515

xyttr::tweet-with-photoを適当なキーに割り当てるか、M-x xyttr::tweet-with-photoで実行します。

http://gyazo.com/e80dbf64a31d31055ac9428682a307d7.png

実装ですが、connect関数使ってベタで書いてるのはxhrだとこの辺の問題で投稿が失敗(500)してしまうため。
Msxml2.XMLHTTP.6.0を使うようxhrを書き換えて試してみましたが、タイムライン系api等のapi.twitter.comへのリクエストは問題なかったものの画像アップロードは403でした。
ちゃんとパケット調べてないけどやはり余計な0x00で問題起きてそう。

read it laterをxyttrで見る (xyttr Advent Calendar 23日目分)

xyzzy xyttr

この記事はxyttr Advent Calendar 2011の記事です。

14日目のタイムラインコマンド自作の応用で、read it laterに貯め込んだURLをxyttrで表示してみます。
read it later viewer for xyttr

ロード後、M-x readitlaterで実行します。
http://gyazo.com/7994e8701b5c3204ad3e776792792469.png

URLの上でmキーを押すと既読マークを付けます、が手抜きなのでステータスバーにメッセージが出るだけで見た目変わりません。起動しなおすとエントリーが消えてます。

もう1つ、read it laterにURLを登録するコマンド ril-focused-url を用意してあります。このコードではr-i-lというキーシーケンスに割り当ててますが適当に変更してお使い下さい。

timeline-draw-statuses (xyttr Advent Calendar 22日目分)

xyzzy xyttr

この記事はxyttr Advent Calendar 2011の記事です。

関数xyttr::timeline-draw-statusesを使った小ネタを2つ。

- *function* timeline-draw-statuses (buf statuses &key (point 0))

    ツイートデータを整形してバッファに出力します。

    * buf -- 出力先バッファ
    * statuses -- ツイートデータのリスト
    * point -- 出力先位置

◆ 栞を挟む

(in-package :xyttr)

;; ツイートデータ生成
(defun make-tweet (status-id name text)
  `(("id" . ,status-id)
    ("user" ("screen_name" . ,name))
    ("text" . ,text)
    ("created_at" . ,(format-date-string "%a %b %d %H:%M:%S %Z %Y"))))

;; カーソル下のツイートの1つ前に栞を挟む
(defun kokomadeyonda2 ()
  (interactive)
  (when (eq buffer-mode 'xyttr-timeline-mode)
    (let ((p (1- (entry-point)))
          (tw (make-tweet nil "xyttr_shiori"
                          (format nil "~V@{◇◆~}"
                                  (ash (window-columns) -2) t))))
      (w/buffer-modifying ()
        (timeline-draw-statuses (selected-buffer) (list tw) :point p)))))

(define-key *xyttr-timeline-keymap* '(#\C-k #\C-m) 'kokomadeyonda2)

http://gyazo.com/81afadeac789639f2089865ebaf30262.png
好きな模様を作って使おう。

◆ トレンドを表示

13日目に紹介したtrends apiを使います。

(define-api trends (woeid exclude)
  :path (format nil "/1/trends/~A.json" woeid))

(defun show-trend ()
  (interactive)
  (when (eq buffer-mode 'xyttr-timeline-mode)
    (let ((buf (selected-buffer)))
      (api-trends-async
       :woeid +woeid-tokyo+
       :onsuccess
       (lambda (res)
         (w/json (trends) (car res)
           (let* ((text (format nil "trends:~{ ~A~}"
                                (mapcar #'(lambda (tr) (json-value tr name))
                                        trends)))
                  (tw (make-tweet nil "xyttr_trend_info" text)))
             (save-excursion
               (set-buffer buf)
               (w/buffer-modifying ()
                 ;; バッファ先頭へ出力
                 (timeline-draw-statuses buf (list tw))
                 (refresh-screen))))))))))

(define-key *xyttr-timeline-keymap* #\t 'show-trend)

http://gyazo.com/f94527999d086b92ee3c8f7b8dacac58.png

トレンドの使い途イマイチ分からん