Hatena::Groupcadr

わだばLisperになる このページをアンテナに追加 RSSフィード

2004 | 12 |
2005 | 01 | 02 | 07 | 10 | 11 |
2006 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2007 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2008 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2009 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2010 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2011 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 11 |

2008-11-19

CLで日記更新 + Outputzで文字数記録

| 18:11 | CLで日記更新 + Outputzで文字数記録 - わだばLisperになる を含むブックマーク はてなブックマーク - CLで日記更新 + Outputzで文字数記録 - わだばLisperになる

このはてなグループでの日記は、CL + SLIME + simple-hatena-mode.elで投稿しているのですが、今日、OutputzがAPIを公開されたそうなので、日記更新と連動させてみることにしました。

といっても、上のページにGaucheでのサンプルがあるので、それをDRAKMAに翻訳しただけという感じで

(let ((uri "http://cadr.g.hatena.ne.jp/g000001/")
      (size xxxx)
      (key "復活の呪文"))
  (http-request (apply #'format nil
                       "http://outputz.com/api/post?uri=~A&size=~D&key=~A" 
                       (mapcar #'hunchentoot:url-encode (list uri size key)))
                       :method :post))
...

のようなコードを投稿プログラムに組み込んで文字数をカウントした結果をAPIに送るという感じです。

ということで、記念カキコです。

2008-05-30

LISP引きこもり生活 (10) SWANK環境

| 22:04 | LISP引きこもり生活 (10) SWANK環境 - わだばLisperになる を含むブックマーク はてなブックマーク - LISP引きこもり生活 (10) SWANK環境 - わだばLisperになる

当初身の回りのものを全部CLで揃える試みを綴る予定だったのですが、どんどんひよってきてしまいました。

もっとアグレッシブに行きたいのですが、なかなか難しい…。

ここ最近で変化したことといえば、SBCLでの開発環境をちょっと変更してみていました。

どういう風に変更したかというと、SLIMEはCL側でSWANKというサーバを立てて、EMACS等のクライアントを通信するという形で開発が行われるわけなのですが、SBCLのSWANKサーバを単体の実行ファイルにしてみました。

起動時に色々設定ファイルを読ませる設定を書いたりするより、すっきりするかと思い試してみているのですが、気分的にはなんとなくすっきりするような…。

あんまり役に立ちそうもないですが、なんとなく手順を書いてみます。

1. SBCLは起動時に実行する関数が指定できるので、この機能を利用して、SWANKサーバを起動させます。

(pushnew
  (lambda () (swank:create-server :port 4005 :dont-close 'T))
   sb-ext:*init-hooks*)

sb-ext:*init-hooks*に起動時に実行する関数をリストで指定します。

2. 他、SWANK関係のいつもの設定をいろいろ

(progn
  (setq sb-impl::*default-external-format* :utf-8)
  (setq swank::*coding-system* "utf-8-unix")
  ;; ...
  )

3. 普段、イメージを作成するのと同じようにしてダンプするところで、実行ファイルにするように指定します。

(sb-ext:save-lisp-and-die "/tmp/swank-sbcl" :purify 'T :executable 'T)

これでできた、/tmp/swank-sbclを実行すれば、SWANKサーバが起動します。

2008-05-24

LISP引きこもり生活 (9) なんとなくDylan

| 18:19 | LISP引きこもり生活 (9) なんとなくDylan - わだばLisperになる を含むブックマーク はてなブックマーク - LISP引きこもり生活 (9) なんとなくDylan - わだばLisperになる

最近も特にLISP引きこもり生活的には、あまり変化がありませんでした。

というか、CLで身の回りのものを固めるという作業が等閑になっているので、どうしたものかという感じです。

なんでか知りませんが、S式Dylanのマニュアルを眺めて、関数やマクロをCLに移植してみたりしています。

まとまったら、CodeReposにアップしてみたいような気もしますが、何の役にも立たないしやめておいた方が良いような気もします(^^;

2008-05-08

LISP引きこもり生活 (7) とにかく全部CLでそろえるという夢

| 20:31 | LISP引きこもり生活 (7) とにかく全部CLでそろえるという夢 - わだばLisperになる を含むブックマーク はてなブックマーク - LISP引きこもり生活 (7) とにかく全部CLでそろえるという夢 - わだばLisperになる

今週は特にLISP引きこもり生活的には、あまり変化の無い週でした。

quekさんのエントリ

を読んで、自分もClimacsで、SLIMEで、SKKで、EMACSいらずな方向に向かいたい!と思いましたが、climacsが上手く起動せず早速挫折しました。

説明が後になりましたが、Climacsとは、CLで実装されたエディタでEMACSみたいなエディタです。

うーん、32bit Ubuntuの時は動いてたんだけどなあ。64bit Ubuntuだと何か問題あるんだろうか…。そんなことないよなあ。

Closureという、CLで実装されたウェブブラウザもあるのですが、これも動かない、32bitの時は動いてたのに、何が違うんだろうなあ。

いずれにせよ、MCCLIMがちゃんと設定できてないんだろうなあ。

2008-04-30

LISP引きこもり生活 (6) はてダラの代わりにSLIME

| 03:07 | LISP引きこもり生活 (6) はてダラの代わりにSLIME - わだばLisperになる を含むブックマーク はてなブックマーク - LISP引きこもり生活 (6) はてダラの代わりにSLIME - わだばLisperになる

CLのイメージの中に引きこもって生活する日々をつづっております。

自分はこのグループの日記は、simple-hatena-modeで書いて、hw.plは使わず、SLIMEを使ってCL経由で投稿しているのですが、content-length関係でたま怒られることがありました。

quekさんのエントリ

でcontent-lengthを指定すること、というのを読んで、そうか指定してないのが原因か!、と思い早速指定してみることにしました。

それとquekさんは、はてなだけでなくBloggerでもエントリを書かれているのですが、EmacsのMuseモードでエントリを書いて、MuseモードからSLIME経由で送信するというエントリを書かれています。

いつもプラクティカルな内容で非常に参考になります!

自分も真似して、simple-hatena-modeでポストする関数を上書きして、SLIMEに命令を送信するという方式に変更してみました。

具体的には、simple-hatena-submitを上書きして、SLIMEに実行させたい式を送るという流れになります。

自分の場合は、SLIMEは起動させっぱなしなので、コードのバッファやREPLから命令していたのですが、やっぱり日記のバッファから送信できた方が便利かなということで…。

ということで、テスト投稿!。…してみたら、内容の先頭に*edit*という印が付いてしまう現象に遭遇。うーん、何で急に*edit*とか付くようになったんだろう…。

(defun simple-hatena-internal-build-command ()
  (format "(hw:post-yyyymmdd-entry \"%s\" \"%s\" :group \"%s\")"
          (buffer-file-name)
          simple-hatena-local-current-buffer-id 
          simple-hatena-local-current-buffer-group))

(defun simple-hatena-submit ()
  "はてなダイアリー/グループに投稿する。"
  (interactive)
  (slime-repl-send-string 
   (simple-hatena-internal-build-command)))

2008-04-24

LISP引きこもり生活 (5) なんでもCL-USERに取り込めの巻

| 02:28 | LISP引きこもり生活 (5) なんでもCL-USERに取り込めの巻 - わだばLisperになる を含むブックマーク はてなブックマーク - LISP引きこもり生活 (5) なんでもCL-USERに取り込めの巻 - わだばLisperになる

前回から2ヶ月ぶり位になってしまいましたが、「LISP引きこもり生活」はこれから続きものということで毎週水曜日に書こうと思っています!

「LISP引きこもり生活」はCLのイメージからできるだけ外出しないようにする試みです。

前回までは、お世話になったzshなどを放棄し、Lispマシン風にSLIMEからなんでも操作しよう!という流れでした。

この2ヶ月で変ったことといえば、シェル操作(OS操作)でzsh以上の便利さがなかなか実現できず、結局段々zsh様を使うことが増えて来ました(笑)

これではいけないな。CLASHとか導入してみようかな…。

それと、生活用にHOMEパッケージというものを定義してみたのですが、割と中途半端な存在で、これだったら、CL-USERにがんがん自作ユーティリティをつっこんでしまっても良いのではないかと思い、なんでもCL-USERに取り込んで、LISPイメージをダンプするようにしました。

ASDFや、ASDF-INSTALLも、USE-PACKAGEしていますが、ぶつかるものもないので普段の生活にはこっちの方が便利かもしれません。

もともとCL-USERはユーザの為の作業空間な気がするので、少々汚なくなっても良いかなと。

もちろんCLパッケージからエクスポートされている関数の名前をつけかえるようなことはまずいですが…。

なんにしろこういうカスタムイメージは作業するのには非常に便利かなと思います。

2008-02-29

LISP引きこもり生活 (4) はてな日記をCLから更新する その2

| 11:02 | LISP引きこもり生活 (4) はてな日記をCLから更新する その2 - わだばLisperになる を含むブックマーク はてなブックマーク - LISP引きこもり生活 (4) はてな日記をCLから更新する その2 - わだばLisperになる

昨日のコードでは、どうもポストする度にどんどん追記されていってしまうようなので、更新する際には一旦削除してから再度ポストすることにしてみました。

もっとちゃんとした正しい方法があると思うんですが…。

ログアウトしてない所為かと思ってログアウトもするようにしたんですが、ログアウトするかしないかは追記の件とは関係ない様子。

上書きモードとか、追記モードがあるのかしら。

;; 使い方

;; Lispマシン的気分のための設定
(login-setq *hw-base-url* "http://cadr.g.hatena.ne.jp/g000001")

;; ポスト
(let ((cj (hw:login))
      (base *hw-base-url*))
  (hw:delete-entry base cj)
  (hw:post base
	   (hw::file-to-string "g000001/group/cadr/2008-02-29.txt")
	   cj)
  (hw:logout cj))

;; ----------------
(defun delete-entry (base-url cj &optional date)
  (let ((date (or date (today-string))))
    ;; confirm
    (http-request (sconc base-url "/edit") 
		    :external-format-in :utf-8 
		    :external-format-out :utf-8
		    :method :post
		    :cookie-jar cj
		    :parameters 
		    `(("mode" . "delete")
		      ("rkm" . ,(rkm cj base-url))
		      ("date" . ,date)))

    ;; delete
    (http-request (sconc base-url "/deletediary") 
		    :external-format-in :utf-8 
		    :external-format-out :utf-8
		    :method :post
		    :cookie-jar cj
		    :parameters 
		    `(("mode" . "enter")
		      ("rkm" . ,(rkm cj base-url))
		      ("date" . ,date)))))

(defun logout (cj)
  (http-request "https://www.hatena.ne.jp/logout" 
		:external-format-in :utf-8 
		:external-format-out :utf-8
		:cookie-jar cj))

2008-02-28

LISP引きこもり生活 (3) はてな日記をCLから更新する

| 18:30 | LISP引きこもり生活 (3) はてな日記をCLから更新する - わだばLisperになる を含むブックマーク はてなブックマーク - LISP引きこもり生活 (3) はてな日記をCLから更新する - わだばLisperになる

ログインシェルをGaucheに変更したら、普通のシェルを呼び出すことが前提の今の自分のEmacsの設定では、Emacs上からのgrepとか色々支障を来たすようになりました。

はてなの日記はSimple Hatenaモードからはてダラを呼び出して更新しているのですが、これもどうやら上手く動かなくなった様子。

ということで、原因を追及するのも良いのですが、CLから日記を更新することにしてみました。

はてな日記用のAPIは公開されていないようなので、適当にHTMLから情報を切り出し。

今のところ投稿することしかできませんが、まあ良しとします。

適当過ぎる作りなので野生味溢れる使用感ですが、いつの日かSimple Hatena Modeから呼び出して更新できるようにしたいところです。

(defpackage :hw
  (:use :cl :drakma :kmrcl))
(in-package :hd)

(declaim (inline sconc))
(defun sconc (&rest args)
  (apply #'concatenate 'string args))

(defun read-password-file (&optional (path (merge-pathnames ".hatena" (user-homedir-pathname))))
  (aif (probe-file path)
       (with-open-file (str it :direction :input)
	 (let ((user-alist (read str nil nil)))
	   (values (cdr (assoc :username user-alist))
		   (cdr (assoc :password user-alist)))))
       (error "初期化ファイル:~~/.hatenaが存在していません。:~A" path)))

(defun login ()
  (let ((cj (make-instance 'cookie-jar)))
    (multiple-value-bind (user password)
	(read-password-file)
      (values cj 
	      (http-request "https://www.hatena.ne.jp/login" 
			    :external-format-in :utf-8 
			    :external-format-out :utf-8
			    :method :post
			    :cookie-jar cj
			    :parameters `(("name" . ,user) 
					  ("password" . ,password)))))))

(defun rkm (cj base-url)        ;rkmってなんだか分からないけど、rkmを取得する
  (aand (nth-value 1
	  (ppcre:scan-to-strings 
	   "rkm.*'(.*)'"
	   (http-request (sconc base-url "/edit") 
			 :external-format-in :utf-8 
			 :external-format-out :utf-8
			 :method :post
			 :cookie-jar cj)))
	(aref it 0)))

(defun today-string ()
  (multiple-value-bind (ig no re d mo y)
      (decode-universal-time (get-universal-time))
    (declare (ignore ig no re))
    (format nil "~D~2,'0D~2,'0D" y mo d)))

(defun post (base-url text cj &optional date)
  (let* ((date (or date (today-string))))
    (ppcre:register-groups-bind (y m d) ("(....)(..)(..)" date)
      (http-request (sconc base-url "/edit") 
		    :external-format-in :utf-8 
		    :external-format-out :utf-8
		    :method :post
		    :cookie-jar cj
		    :parameters 
		    `(("mode" . "enter")
		      ("rkm" . ,(rkm cj base-url))
		      ("date" . ,d)
		      ("trivial" . "0")
		      ("year" . ,y)
		      ("month" . ,m)
		      ("day" . ,d)
		      ("title" . "")
		      ("body" . ,text))))))

(defun file-to-string (file)
  (format nil "~{~A~^~%~}" 
	  (series:collect 
	    (series:scan-file file #'read-line))))

;; 使い方
(setq base-url "http://cadr.g.hatena.ne.jp/g000001")
(setq *cj* (login))			;ログインして、cookie jarにクッキー保存

; ポスト
(post base-url (file-to-string "/home/g000001/hatena/g000001/group/cadr/2008-02-28.txt")
      *cj*)

2008-02-25

LISP引きこもり生活 (2) LispMに少しでも近付きたい年頃

| 15:28 | LISP引きこもり生活 (2) LispMに少しでも近付きたい年頃 - わだばLisperになる を含むブックマーク はてなブックマーク - LISP引きこもり生活 (2) LispMに少しでも近付きたい年頃 - わだばLisperになる

とりあえず、前回OSの操作はできるだけSLIMEから操作して、Lispのイメージから外出することは控える、と心に決めました。

今回は、やはり気分が一番大事なので、Lispマシンを操作している感を演出するため、Lispマシンのログイン周りを真似てみようかと思います。

Lispマシンはもともとはシングルユーザ前提で開発がスタートしたこともあるのかユーザ管理については比較的素朴なようです。

とりあえず最初期のMIT CADRのマニュアルを眺めながら、Lispマシンを真似して適当に作ります。

まずは、ログインですが、loginを使い、ユーザ名の設定と、初期化ファイル(ユーザディレクトリのlispm.init)の読み込みをします。

対になるのは、logoutで、これは、ユーザが設定した変数等をログアウト時に元に戻すもののようです。

元に戻す方法は、非常に素朴で、ユーザのログイン時のみ有効な変数を設定する専用の関数を使用し、その関数は、値の設定と同時に、設定をアンドゥする式をlogout-listというリストに格納します。

そして、ログアウト時にそのリストをevalすることによって元に戻すということになっています。

ログイン変数用にlogin-setq、他の汎用的なログイン時の設定変更にはlogin-evalがあり、主に初期化ファイル内で使用します。

ちなみにTAOには、セミグローバル変数という、ユーザがログインしている間だけ有効という変ったものがあったようです。

ということで、マニュアルの説明から適当に想像して作ってみました!

Lispマシンのソースを見て作ったわけではないので、定義については適当です…。そのうちちゃんと読んでみようかと…。

ただ単にログインしただけでは、寂し過ぎるので、とりあえず、Stumpwm(Common Lispのウィンドウマネージャ)とemacsclientの組み合わせを活かして未読メールがある場合は、Wanderlustを起動するようにしてみました。(Emacs側ではWanderlustをenv-mailという関数でラッピングしています)

;; 動作

(setq foo 777)
foo
;=> 777

(login 'g000001)
;=>
;11件の未読メールがあります。メーラーを起動しますか? (y or n)
;        〜〜Wanderlust起動〜〜
;Hi.

(login-setq foo 33 bar 22)

foo
;=> 33

logout-list
;=> ((MAKUNBOUND 'BAR)(SETQ FOO 777)(MAKUNBOUND 'WATCH-MAILDIR) )

;(logout)

foo
;=> 777

bar
;=> error

;; 真似
(defparameter user-id ""
  "The value of user-id is either the name of the logged in user, 
as a string, or else an empty string if there is no user logged in.
 It appears in the who-line.")

;logout-list Variable
(defparameter logout-list ()
  "The value of logout-list is a list of forms 
which are evaluated when a user logs out.")

(defun login (name &optional (load-init))
  "If anyone is logged into the machine, login logs him out.
 (See logout .) Then user-id is set from name.
 Finally login attempts to find your INIT file. 
It first looks in \"user-id ; .LISPM (INIT)\", then in \"(INIT); 
user-id .LISPM\", and finally in the default init file
 \"(INIT); * .LISPM\". When it finds one of these that exists,
 it loads it in. login returns t ."
  (setq user-id (string name))
  (unless load-init
    (load (merge-pathnames "lispm.init" (user-homedir-pathname)))))

(defun logout (&optional name)
  "First, logout evaluates the forms on logout-list.
 Then it tries to find a file to run, looking first in 
\"user-id ; .LSPM_ (INIT)\", then in \"(INIT); user-id .LSPM_\", 
and finally in the default file \"(INIT); * .LSPM_\". 
If and when it finds one it these that exists, 
it loads it in. Then it sets user-id to an empty string and
 logout-list to nil , and returns t ."
  (declare (ignore name))
  (setq user-id "")
  (eval `(progn ,@logout-list))
  (setq logout-list () ))

(defmacro setq-return-undo (var val)
  "setqを実行し、実行内容をアンドゥする式を返す。2値目は、setqの返り値"
  `(let ((undo (if (boundp ',var)
		   '(setq ,var ',(and (boundp var) (symbol-value var)))
		   '(makunbound ',var))))
     (push undo logout-list)
     (values undo (setq ,var ,val))))

(defmacro login-setq (&rest form)
  "login-setq is like setq except that it puts a setq form on
 logout-list to set the variables to their previous values."
  `(progn
     ,@(do ((l form (cddr l))
	    (res () (cons `(nth-value 1 (setq-return-undo ,(car l) ,(cadr l)))
			  res)))
	   ((endp l) (nreverse res)))))

(defmacro login-eval (&rest form)
  "login-eval is used for functions which are \"meant to be called\" 
from INIT files, such as eine:ed-redefine-keys, 
which conveniently return a form to undo what they did.
 login-eval adds the result of the form x to the logout-list."
  `(progn
     ,@(loop :for l :in form
	     :collect `(push ,l logout-list))))

lispm.initの例

;; -*- lisp -*-

(in-package :home)

(login-setq watch-maildir '("/home/g000001/Maildir/inbox/new/*.*"
			    "/home/g000001/Maildir/WebApp/new/*.*"))
(defun mail ()
  (kmrcl:command-output "~A ~A" "emacsclient -e" "'(wl)'"))

(defun 未読さん ()
  (let ((mails (reduce (lambda (res x) (+ (length (directory x)) res))
		       watch-maildir
		       :initial-value 0)))
    (cond ((zerop mails)
	   (format t "未読メールはありません。~%"))
	  ('T (when (y-or-n-p "~D件の未読メールがあります。メーラーを起動しますか?" mails)
		(mail))))))

;;
(未読さん)
(format t "Hi.~%")

2008-02-24

LISP引きこもり生活 (1)

| 05:09 | LISP引きこもり生活 (1) - わだばLisperになる を含むブックマーク はてなブックマーク - LISP引きこもり生活 (1) - わだばLisperになる

私は実生活では、生きることに疲れ、貯金を食い潰しつつ、ひきこもり生活をしているわけなのですが、一歩進んで、コンピュータ環境的にはLISPに引きこもることにしてみました。なんとなく、面白そうなので。

具体的にどういうことかというと、理想としての最終引きこもり形態としては、Lispマシン上から一歩も外に出ないような生活となります。

しかし、そんなことは現実問題として無理なので、この理想にどうにか近付きつつ、現状の快適さもできるだけ損なわない、というのを目標にしようかと思います。

色々段階はあるかと思いますが、始めてみないことには分からないので、とりあえずLISP系のアプリケーションで代替できそうなものは徐々に置き換えて行くことにします。

まず、最初に何から置き換えようかと考えたのですが、やはり、ログインシェルかなと思います。

Lispマシン上で日常作業はどのように行われていたのかは、知る由もないのですが、なんとなくの私の見当では、UNIXのように、インタラクティブにシェルを操作するのではなくて、エディタのバッファ上から、色々な操作を行っていたのではないかなと想像しています。

実際Lispマシンのエミュレータを使ってみても、エディタ上のLispの式を評価することによってOSを操作できるので、エディタ上で大概のことは操作できてしまいます。

なんというか、Emacsの*scratch*バッファで、OSを全部操作するような感じで。

恐らく、シェルのインタラクティブ性というものは、端末からログインして作業する、ということを前提に発展してきたのだと思いますが、Lispマシンはその辺の考え方も違っていそうです。この辺は現状でも生き残っているSmallTalkはどうなっているのか興味があるところですね。

それで、エディタからなんでも操作するのを実現する方法なのですが、SLIMEを使っていて、非常にこれに近いものを感じるので、何でもSLIMEごしに作業することにしてみました。

そしてそれと同時に、便利なZshの使用を停止し、SLIMEから操作した方が楽な状況に強制的に追い込むことにしようということで、ログインシェルをSCSHとGaucheに変更。Zshよ、8年間ありがとう…。

そのうち、initをCLの処理系に置き換えられればと思いますが、さすがに無理かな(笑)

とりあえず、ログインシェルは置き換えたので、SLIMEから操作するということは、どういうことになるのか、適当に何か作って試してみることに。

第1弾として、wgetもどきを作ってみました。

この位なら、まだ手の届くところかなとは思いますが、システム管理とかどうするんだろう…(´▽`*)…。

;; 使用例
(wget "http://prdownloads.sourceforge.net/scsh/scsh-0.6.7.tar.gz" "/var/tmp/")
;=>
; http://prdownloads.sourceforge.net/scsh/scsh-0.6.7.tar.gz ==> /var/tmp/scsh-0.6.7.tar.gz
; ....................................................................................................102400
; ....................................................................................................204800
; ...

;; 定義
(defpackage :home 
  (:use :cl)
  (:export :wget))

(in-package :home)

(defun wget (uri &optional (dir "./"))
  (let* ((file-name (aref (nth-value 1 (ppcre:scan-to-strings ".*/([^/]*)$" uri)) 0))
	 (out-file (concatenate 'string dir file-name)))
    (format t "~A ==> ~A~%" uri out-file)
    (with-open-file (out out-file
			 :direction :output 
			 :if-exists :supersede
			 :element-type 'unsigned-byte)
      (with-open-stream (str (drakma:http-request uri :want-stream 'T))
	(do ((s (read-byte str nil -1) (read-byte str nil -1))
	     (cnt 0 (1+ cnt)))
	    ((= -1 s) (format t "end ~A.~%" cnt))
	  (write-byte s out)
	  (when (and (zerop (rem cnt 1024)) (not (zerop cnt)))
	    (princ ".")
	    (when (zerop (rem cnt (* 100 1024)))
	      (format t "~A~%" cnt))))))))