ラベル elisp の投稿を表示しています。 すべての投稿を表示
ラベル elisp の投稿を表示しています。 すべての投稿を表示

2021年6月15日火曜日

pathname

 Windows環境ではなんだかんだとunzipが面倒臭い。
 Emacsから外部アプリケーションを起動してコマンドひとつで、unzipできるようにしているのだけど、外部アプリケーションは非同期で、動いているので、unzipしたあとの作業が自動化しづらい。できなくはないけれど、Emacsから同期してunzipができれば、それが一番だ。
 そこで slime-evalを使ってABCL経由でunzipすることにした。
 ABCLはjavaで作られていることもあって内蔵のunzipがあるのだ。

 で、やってみたら落ちた。
 slime-evalがエラーになっていた。
 ——なんで?
 調べてみると、どうやらABCLのunzipは展開したファイルのlistを返却してくれていてそれが原因らしい。というのもlistの中身はpathnameで「#P"/XXXX/XXX"」みたいになっているのだが、これをelispが解釈できなかった。
 目が点だ。
 pathnameって中身はstringじゃなかったの?

CL-USER> (stringp #P"/tmp/a")
NIL
CL-USER> (pathnamep #P"/tmp/a")
T
CL-USER> 

 ちがう。なんてこった。
 stringに変換する関数を探す。一番、時間がかかった。見つけた。
 ——namestring

CL-USER> (namestring #P"/tmp/a")
"/tmp/a"
CL-USER> 

 これさえあれば、listの中身を一括変換するのはmapcarで一発である。

2021年6月8日火曜日

PythonとLispのリストのことなど

 Windows版のEmacsを見ていたらPythonが同梱されていた。
 オライリーのPython本がでたとき、手をつけてみようと思ったのだけど、なんだかんだとやらなかったので、ちょっと心理的な敷居は高いのだけれど、LispハッカーのPeter Norvigも最近はPythonを使っているようだし。

 たとえば、PythonにもListがあるのだけれど。

>>>  wrk =[1,2,3,4]
 wrk =[1,2,3,4]
>>> wrk
wrk
[1, 2, 3, 4]
>>> 

 Listの途中の値を変更するにはこうするらしい。

>>>  wrk[2]=99
 wrk[2]=99
>>> wrk
wrk
[1, 2, 99, 4]
>>> 

 そういえば、lispには直接、Listを変更する構文はないなぁ。そのかわりにsetfというものがある。

( setq wrk '(1 2 3 4))
(1 2 3 4)

wrk
(1 2 3 4)

( setf (elt wrk 2) 99)
99

wrk
(1 2 99 4)

 どちらが読み易いか、というと、Pythonの方だろうなぁ。setfは変態すぎる。

(setq wrk  "ABCDEF")
 "ABCDEF"

wrk
 "ABCDEF"

(setf (substring wrk 2 3)  "Z")
 "ABZDEF"

wrk
 "ABZDEF"

 こんなことまでできるし。
 まぁ、この程度ならPythonでも文字列のあつかいがimmutableでなければ、できるんだろうけど。
 Pythonそのものがサポートしないといけない。
 ところが、setfってたしか、マクロだからLispが構文的にサポートしているわけじゃないんだよなぁ。

2020年9月18日金曜日

ループ処理

 C言語を使っていたころはループはforばかりを使っていた。
 それがPerlでforeachを知り、これは良い! と思っていたのだけど。
 最近、javaでforeachループを(forだけど)使っていて、なんか、今いちだなぁ、と。
 CommonLispの loopマクロを知ってしまった今となっては。javaにはどうしてloopマクロがないんだ、と不満がつのってしまう。ストレスを感じる。
 なぜだろう。
 loopマクロだと感覚的にループ処理を書いていると、あまり思ってないような気がする。普通の処理を書いているような感じ。不思議だ。
 たぶんループの中で行なわれる処理と、ループ自体の処理が渾然しているからかもしれない。つながっている。
 forループとかだと、ループ自体の処理と、中の処理を頭の中で意識的に無理してつないでいるような感覚があるんだよな。loopマクロだと、それが自然に書き下していけるような感じ。

2020年9月4日金曜日

replot

 ほぼ毎日、記録するようになったのは 炭水化物を制限するようになってからだけど、そもそも体重を記録するようになったのは遠州灘で体重計を買ってからだ。データはテキストで管理してグラフ表示には gnuplotを使っている。

 で、gnuplotには表示するデータの範囲して表示しなおす機能がある。
 いちいち一年分のデータとか、つくりなおして実行する必要がない。

gnuplot> set xrange ["2019-8-30":]
gnuplot> replot

 gnuplotのバッファ 1にいちいち、それらのコマンドを入力するは面倒なので、Emacsから簡単にできるようにしようとしたらすでに「y-replot」という関数が定義してあった。
 まったく記憶にない。
 過去の自分がつくったにちがいないのだけれど。

Footnotes:

1

Emacsから使っているので。

2020年8月28日金曜日

nth

 

え?

ええ?

CL-USER> (setf wrk '(1 2 3))
(1 2 3)
CL-USER> (nth 1 wrk)
2
CL-USER> 

そうだったのか!

2020年5月19日火曜日

if文に隠蔽される処理

 たとえば、変数の値によって関数の呼び出しをかえるような処理は、普通のプログラミング言語だと

 if(変数A == 999){
    処理関数(1);
}  else {
    処理関数(2);
}

 みたいな書き方をする。
 もちろん、Lisp系でも同じような書き方ができるけれど

( if (= 変数A 999)
    (処理関数 1)
    (処理関数 2))

 こんな書き方もできる。

(処理関数 ( if (= 変数A 999)
              1
              2))

 後者の方が見易いような気がする。というのも

(処理A)
( if (条件)
    (処理B 1)
    (処理B 2))
(処理C)

 となっていると、if文の中に処理がほんのわずかに隠されて処理の流れが見づらなるような。

(処理A)
(処理B ( if (条件)
           1
           2))
(処理C)

 ね?

2020年5月18日月曜日

mapcarとloop

 elispを独習しはじめたころ、おおっ、と感動したのは「mapcar」の存在だった。
 何を感動したのか、というと、それがループ処理だった、ということ。よくあるバグにループの脱出条件のミスって永久ループしてしまう、というのがあるけれど、これならかならず、終了するじゃないか。なので最初のころ書いたコードは「mapcar」だらけになってしまった。
 ところがある日、そのコードを読み返していて気づいた。
 ——読みづらい。
 「mapcar」の引数は

(mapcar 処理 リストを生成する処理)

 となっているので、処理の順番という意味ではうしろから読み解かなくていけないのだ。
 それが脳みそに余計な負荷をかけている。
 もちろんすぐにloopマクロの存在に気づいて、今ではloopマクロを使いまくりなのだけど。

( loop for x in リストを生成する処理
      collect 処理)

 こちらの方が「リストを生成する処理」「処理」と読んでいけるのでものすごく楽。
 今や「mapcar」はワンラインで書けるサイズでないと、書く気がしない、という。

2020年5月1日金曜日

or

 ぱらぱらとEmacsのライブラリの中身を眺めていたら次のようなコードをぶち当たった。

(goto-char ( or point1 point2))

 もちろんこのままじゃないけれど。
 わかるよ。point1か、point2が設定されているところへカーソルを移動する。うん。
 あんまりあっさり書けてしまっていたのでひっくり返ってしまった。

2020年4月30日木曜日

二画面分割を左右分割から上下分割へ切り替える

  Dell 27 MonitorになってからEmacsを左右二画面分割して使うことが多くなった。
 横長だからだけど、それでも時々、上下二分割にしたいことがある。しかも左右二分割した画面を、上下に切り替えたい。そんなコマンドはないものか、と探してみたけれど、見つけることができなかった。いちいちCtrl+x,1で全画面化して二分割して……ああ、面倒臭い。

 つくる。
 適当に配置をrotateさせるのでたぶん、4回、呼び出すと、元の位置に戻るはず。
 それにしてもダサいな、このコード……。

2020年3月10日火曜日

数独プログラム

 「 あらゆる数独パズルを解く」はPeter Norvigなのに、Pythonで書かれている。
 じゃあというわけでelispで書いてみた。

(2) ある値に対し、ユニットの中で置ける場所が1つしかないなら、その値をそこに置く。

 は、うまく動かなかったのでコメントアウトしているけど。
 それでも「 あらゆる数独パズルを解く」のページの問題はいちおう解けたからだいじょうぶだと思うけど。
 まぁ、よいか。

( defun  test ()
  ( progn (sudoku-init)
(sudoku-set  "
 . . 5 |3 . . |. . . 
 8 . . |. . . |. 2 . 
 . 7 . |. 1 . |5 . . 
 ------+------+------
 4 . . |. . 5 |3 . . 
 . 1 . |. 7 . |. . 6 
 . . 3 |2 . . |. 8 . 
 ------+------+------
 . 6 . |5 . . |. . 9 
 . . 4 |. . . |. 3 . 
 . . . |. . 9 |7 . . 
 ")
       (sudoku-solve)
       (sudoku-print)))

(benchmark 1 '(test))
| 1 4 5 | 3 2 7 | 6 9 8 |
| 8 3 9 | 6 5 4 | 1 2 7 |
| 6 7 2 | 9 1 8 | 5 4 3 |
|-------+-------+-------|
| 4 9 6 | 1 8 5 | 3 7 2 |
| 2 1 8 | 4 7 3 | 9 5 6 |
| 7 5 3 | 2 9 6 | 4 8 1 |
|-------+-------+-------|
| 3 6 7 | 5 4 2 | 8 1 9 |
| 9 8 4 | 7 6 1 | 2 3 5 |
| 5 2 1 | 8 3 9 | 7 6 4 |
 "Elapsed time: 0.179490s (0.093774s in 1 GCs)"

 ベンチマークもそこそこ。

2020年3月6日金曜日

dash.el(2)

 たとえば、(1 2 3)と(4 5 6)を組み合わせたい。

((1 . 4) (2 . 4) (3 . 4) (1 . 5) (2 . 5) (3 . 5) (1 . 6) (2 . 6) (3 . 6))

 みたいな感じに。
 そこで

( defun  y-combination-of-list (list1 list2)
   "list1の要素とlist2の要素を組み合わせた結果をconsして出力する。"
  ( loop for x in list1
        collect ( loop for y in list2
                      collect (cons x y))))

 というのをでっちあげた。でもこれだと結果は

(((1 . 4) (1 . 5) (1 . 6)) ((2 . 4) (2 . 5) (2 . 6)) ((3 . 4) (3 . 5) (3 . 6)))

 という風になってしまう。
 これを入れ子のリストではなく、フラットなリストにしたい。
 それで「 dash.el」の「-flatten」が必要だったのだけど。

(-flatten '(((1 . 4) (1 . 5) (1 . 6)) ((2 . 4) (2 . 5) (2 . 6)) ((3 . 4) (3 . 5) (3 . 6))))
((1 . 4) (1 . 5) (1 . 6) (2 . 4) (2 . 5) (2 . 6) (3 . 4) (3 . 5) (3 . 6))

 よくよく考えたら「dash.el」に、そもそもそんな関数があるんじゃないか? つらつら「 https://github.com/magnars/dash.el#functions」を見ていたら。

(-table-flat 'cons '(1 2 3) '(4 5 6))
((1 . 4) (2 . 4) (3 . 4) (1 . 5) (2 . 5) (3 . 5) (1 . 6) (2 . 6) (3 . 6))

 あった。

2020年3月5日木曜日

dash.el

 どのパッケージがきっかけでインストールされたのか、わからないけれど、「dash.el」がインストールされている。

;;; dash.el --- A modern list library for Emacs

 だそうだ。
 「-flatten」というのがあってお気に入り。

(-flatten '((1 2 3)(4 5 6)))
(1 2 3 4 5 6)

 で、素の ABCLを使っていて「flatten」が必要になった。
 「alexandria」をインストールすれば、いいんだろうが、諸事情というものがある。
 「dash.el」の「-flatten」が使えないもんだろうか。
 「–map」「–mapcat」「-mapcat」「-flatten」を喰わせてみたところ。

(-flatten '((1 2 3)(4 5 6)))
(1 2 3 4 5 6)

 動いた。

2020年3月3日火曜日

async.el

 helmをインストールすると、もれなくasync.elがついてくる。
 Emacsの非同期ライブラリである。
 バックグラウンドでEmacsをもうひとつ、立ち上げてそちらで処理させる、というもの。

( setq wrk (async-start
           ( lambda ()
             222)))
-- 割愛 --

(async-get wrk)
222

 システムモニタを立ち上げておくと、Emacsがもうひとつ、起動されるのがわかる。
 100個とか、無理だろうけど 1

( setq wrk ( loop for x from 1 to 10
                collect (async-start
                         `( lambda ()
                            ,x))))
-- 割愛 --

( loop for x in wrk
      collect (async-get x))
(1 2 3 4 5 6 7 8 9 10)

 カンマを使えば、変数の中身を展開できるけど、さすがにクロージャーでデータを渡すのは無理だろうなぁ。別プロセスだもんなぁ。スレッドならできるのだろうけど。
 ——と思ったけれど、やってみたらできた。
 驚いた。

( setq lexical-binding t)
t

( defun  wrk (x)
  ( lambda()
    x))
wrk

( setq wrk ( loop for x from 1 to 10
                collect (async-start (wrk x))))
-- 割愛 --

( loop for x in wrk
      collect (async-get x))
(1 2 3 4 5 6 7 8 9 10)

 なんで?

(wrk 1)
(closure ((x . 1) t) nil x)

 ああ、クロージャーはリストに展開されるんだ。
 なるほど。それならpipe渡しでも可能だ。あれ? ということはelispだからできるってこと?
 ほえぇ。elispはあなどれない。

Footnotes:

1

Emacs for Windowsでは10個でエラーになった。

2020年2月28日金曜日

slime-transpose-lists

 たとえば

((1 2 3)
 (4 5 6)
 (7 8 9))

 を

((1 4 7)
 (2 5 8)
 (3 6 9))

 という風に縦横を変換したい。
 パッと考えて二重ループで……とごちゃごちゃ考えたのだけれど、面倒臭い。きっとどこかにありそうだ。elisp、CommonLisp関連でググってもなかなか、見つからず、きっと名前に「tranpose」が入っているにちがいない、と考えてEmacsの「describe-function」で見つけた。

(slime-transpose-lists LIST-OF-LISTS)

Not documented.

 まさにこれ。ソースを見たら

( defun  slime-transpose-lists (list-of-lists)
  ( let ((ncols (length (car list-of-lists))))
    ( cl-loop for col-index below ncols
             collect ( cl-loop for row in list-of-lists
                              collect (elt row col-index)))))

 やはり二重ループ。
 それでもLispだとたったこれだけの行数で書ける。
 というか、簡単に書けてしまうから標準でなかったのかも。

2020年2月27日木曜日

(file-expand-wildcards "**/*.*")

 ああ、知らなかっただよ。

(file-expand-wildcards  "**/*.*")

 で、カレントディレクトリの下のファイルが全部、取得できるとは。
 eshellの「**/*.*」はこれは使っているんだろうなぁ。
 今さら気づくおれ。

2020年2月10日月曜日

elispでqueueもどき

 もしかしたら。
 threadとmutexの組み合わせたらキューっぽいことができるんじゃないだろうか?

( let ((lexical-binding t)
      (w (make-mutex)))
  (mutex-lock w)
  ( loop for x from 1 to  10
        collect
        (make-thread
         ( let ((y x))
           ( lambda()
             ( with-mutex w
               (insert (number-to-string y) " \n"))))))
    (mutex-unlock w))
nil
3 
4 
5 
6 
7 
8 
9 
10 
1 
2 

 処理順序は保証されていないけど、キューっぽいよな、これ。
 うん。

2020年1月28日火曜日

flet(2)

 cl-fletがあれば、レキシカルスコープとあわせてコードを分離できるじゃないか。「use」できないけれど、簡単なパッケージみたいなもんだよなぁ。よろこんで多用していたら——。
 あれ? 再帰呼び出しができない。ふうん、「cl-labels」を使え、ということらしいな。

FUNC is defined only within FORM, not BODY, so you can’t write
recursive function definitions. Use ‘cl-labels’ for that. See
info node ‘(cl) Function Bindings’ for details.

 ふむ。

2020年1月27日月曜日

Org-mode:(org-element-context)

 Org-modeでカレント行のTAGを取得したいことがあって自前でいろいろやっていたんだけれど。

(org-element-context)

 で取得できることに気づいた。
 こりゃあ、いい。

2020年1月24日金曜日

Org-mode:(org-edit-special)

 Org-modeは高機能だということは知っていたけど——だから知らない機能がけっこうあって。
 たとえば、よくソースの断片をメモしているのだけれど。

#+BEGIN_SRC elisp
(messge "ERROR")
#+END_SRC

 Org-modeの中だと、メジャーモードの機能が使えないので、別のところで修正してカット&ペーストでもってきたりしていた。
 ところが「Ctrl+'」とやると、別バッファで開いてくれる。そのうえ、指定したモードにしてくれる。上の例だと「elisp-mode」 1になる。

 これの何がいいのか、というと、SRCで指定しているモードは、Emacsのメジャーモードならなんでもいいはずなので、picture-modeとか、指定できるのだ。SRCの外だと、書いたpictureは勝手に整形されてしまうからこれは重宝する。
 SRCの中に置いておいて修正するときは「Ctrl+'」とやるわけ。

#+BEGIN_SRC picture
  -------+
         |
   <=====+
#+END_SRC

 もちろん、Org-modeも指定できるわけで無意味だけど、それはそれでおもしろい。

Footnotes:

1

正確には(emacs-lisp-mode)。

2020年1月22日水曜日

loopマクロ

 たとえば、あるlistの中から奇数だけを抽出したい、というとき。

wrk
(1 2 3 4 5 6 7 8 9 10)
(loop for x in wrk
      collect
      (when (oddp x)
        x))
(1 nil 3 nil 5 nil 7 nil 9 nil)

 こうやると、nilがまじってしまう。このケースだと、remove-ifを使えば、いいのだけの話なのだけど——loopマクロでこんな風にしていた。

(loop for x in wrk
      append
      (when (oddp x)
        (list x)))
(1 3 5 7 9)

 なんかいまいちだなぁ。
 なんとこうできるのだった。

(loop for x in wrk
      when (oddp x)
        collect x)
(1 3 5 7 9)

 すげえな、loopマクロ。