2017年1月12日木曜日

MobileOrg で Agenda ファイルを org-directry 以外の場所に置くと何が起こるか?   gblog

1.1 はじめに

何やらMobileOrgの同期がうまくいかないなぁ,と思ったときについカッとなってコードを読んだ時の簡単なメモです.

※以下,本文中では,Emacs のシンボル(関数や変数)は(シンボル)で書いてます


1.2 何が起きる?

  • (org-mobile-push) はうまくいく (Emacs –> MobileOrg アプリ)

    • (org-agenda-file) でファイルのフルパスを指定していれば.

  • (org-mobile-pull) はうまくいなかい (MobileOrg アプリ –> Emacs)

片方だけうまくいくおかげで,問題に気づくのにちょっと時間かかった.

1.3 なぜなのか?

1.3.1 org-mobile-pull の挙動

早い話が, (org-mobile-pull) は Agenda ファイルが (org-directory) 以下にあることを想定している


  • (org-mobile-pull) は独自のフォーマットで差分を読み込み,パースして,適用する.

    • Dropbox で同期していれば,MobileOrg アプリは差分情報を (org-mobile-directory)/(org-mobile-capture-file) に保存する

      • (org-mobile-directory) は設定が必要なので,init.el などで設定しているはず.

      • (org-mobile-capture-file) はデフォルトだと,"mobileorg.org"

    • (org-mobile-pull) 時に,(org-mobile-capture-file) から(org-mobile-inbox-for-pull) へコピーする

      • (org-mobile-index-for-pull) のデフォルト値は "~/org/from-mobile.org"

    • この差分を記述するフォーマットはともかく,変更箇所の記述方法が問題

      =~[[olp:<ファイル名>:H1/H2/H3][Head名]]~=
      
      となっている.

    • これを,(org-mobile-locate-entry) が実際のファイルパスとポジションに変換する.

      • このとき,ファイルパスは (org-directry) /<ファイル名>になる.

        • (org-directory) はデフォルトで "~/org"

1.3.2 org-mobile-push の挙動

じゃあ,push はどうなのよというと,実は Agenda ファイルが (org-directory) 以下になくても動くようになっている


  • (org-mobile-push) は.org ファイルを丸々転送している.

    • コピー元は, (org-agenda-files) からファイルパスごと 取ってくる

    • 暗号化しているときは,(org-mobile-capture-file) 経由で暗号化して転送するっぽい

    • でも,この時も .org は暗号化せずに転送しているように見えるけど,大丈夫か?(動かしたわけじゃないから実際はどうなのかわからないけども)


1.4 おわりに

Agenda ファイルはちゃんと,(org-directory) 以下に置きましょう.

どうしてもいやなら,MobileOrg か org-mobile.el を改造しましょう.

2015年4月9日木曜日

resize2fs と fdisk で ext4 のパーティションを縮小する


1 resize2fs と fdisk で ext4 のパーティションを縮小する




1.1 はじめに



パーティションの拡大縮小はいつもGParted を使っていた.

しかし,今回はディスプレイが繋がっていないマシンのパーティションを変更するために,CLI で操作できるresize2fs と fdisk で操作した.

結構悩んだが,わかればそんなにややこしい手順じゃないはず.






1.2 作業の流れ



パーティションを縮小すると言っても,実はパーティションのサイズだけ縮小してもダメらしい.

ファイルシステムのサイズも変えないといけないんだそうだ.

しかも,この2つのサイズが一致してないとまずいらしい.



resize2fs はサイズ指定なしで実行すると,ファイルシステムのサイズを可能な限り大きくする.

つまり,パーティションのサイズいっぱいにファイルシステムのサイズを広げてくれる.

これを利用すれば,面倒な計算なしに,ファイルシステムとパーティションサイズのサイズを合わせられる.

(逆にパーティションサイズに合わせて縮小,ということはやってくれないっぽい?)



というわけで,以下の手順を取る.



  1. resize2fs でファイルシステムを縮小(予定サイズよりもちょっと小さめに)


  2. fdisk でパーティションのサイズを縮小(予定サイズに縮小)


  3. resize2fs でファイルシステムのサイズをパーティションのサイズに合わせる(勝手に計算してくれる)







1.3 resize2fs でファイルシステム



今回は/dev/sdb1 を512GB から32GB に縮小する例を示す.



まずは,resize2fs でファイルシステムのサイズを予定サイズより少し小さめに変更.

512GB–>31GB の変更は結構時間がかかる(10分くらい?).



# e2fsck -f /dev/sdb1
e2fsck 1.42.5 (29-Jul-2012)
Pass 1: Checking inodes, blocks, and sizes
Pass 2: Checking directory structure
Pass 3: Checking directory connectivity
Pass 4: Checking reference counts
Pass 5: Checking group summary information
/dev/sdb1: 184019/30777344 files (1.9% non-contiguous), 7022898/123088640 blocks
# resize2fs /dev/sdb1 31G
resize2fs 1.42.5 (29-Jul-2012)
Resizing the filesystem on /dev/sdb1 to 8388608 (4k) blocks.
The filesystem on /dev/sdb1 is now 8388608 blocks long.
#


次にfdisk でパーティションのサイズを変更する.

操作的にはサイズを変更というより,元のパーティションを削除して,同じところに小さいサイズのパーティションを作りなおすと,なる.



# fdisk /dev/sdb
The device presents a logical sector size that is smaller than
the physical sector size. Aligning to a physical sector (or optimal
I/O) size boundary is recommended, or performance may be impacted.

Command (m for help): p

Disk /dev/sdb: 512.1 GB, 512110190592 bytes
255 heads, 63 sectors/track, 62260 cylinders, total 1000215216 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 4096 bytes
Disk identifier: 0x000c3f6e

Device Boot Start End Blocks Id System
/dev/sdb1 * 2048 984711167 492354560 83 Linux
/dev/sdb2 984713214 1000214527 7750657 5 Extended
Partition 2 does not start on physical sector boundary.
/dev/sdb5 984713216 1000214527 7750656 82 Linux swap / Solaris

Command (m for help): d <-- パーティション削除
Partition number (1-5): 1

Command (m for help): n <-- 新しいパーティション作成
Partition type:
p primary (0 primary, 1 extended, 3 free)
l logical (numbered from 5)
Select (default p):
Using default response p
Partition number (1-4, default 1):
Using default value 1
First sector (2048-1000215215, default 2048):
Using default value 2048
Last sector, +sectors or +size{K,M,G} (2048-984713213, default 984713213): +32G

Command (m for help): p

Disk /dev/sdb: 512.1 GB, 512110190592 bytes
255 heads, 63 sectors/track, 62260 cylinders, total 1000215216 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 4096 bytes
Disk identifier: 0x000c3f6e

Device Boot Start End Blocks Id System
/dev/sdb1 2048 69208063 34603008 83 Linux
/dev/sdb2 984713214 1000214527 7750657 5 Extended
Partition 2 does not start on physical sector boundary.
/dev/sdb5 984713216 1000214527 7750656 82 Linux swap / Solaris

Command (m for help): w
The partition table has been altered!

Calling ioctl() to re-read partition table.
Syncing disks.
#


最後にファイルシステムのサイズをパーティションのサイズに合わせる.



# resize2fs /dev/sdb1
resize2fs 1.42.5 (29-Jul-2012)
Resizing the filesystem on /dev/sdb1 to 8650752 (4k) blocks.
The filesystem on /dev/sdb1 is now 8650752 blocks long.
#


これで完了(のはず)






1.4 おわりに



パーティションの操作にあまり慣れていなかったので,結構調べるのに時間かかった.






--
My Emacs Files At GitHub

Debian でQEMU をビルド


1 Debian でQEMU をビルド




1.1 はじめに



QEMU の環境を何度も作る機会があったので,書いておく.





1.2 参考ページ



Debian上にOSvビルド環境を構築してみた - 夜はいよいよ冴えたのだ。

記事自体はOSv の環境構築について書いているものであるが,その一部としてQEMU のビルドの方法が書かれている.

その部分をありがたく参考にさせていただいた.






1.3 環境




# cat /etc/debian_version
7.8
# uname -a
Linux kvm-host2 3.2.0-4-amd64 #1 SMP Debian 3.2.65-1+deb7u2 x86_64 GNU/Linux





1.4 手順



まずはqemu のソースコードを入手する.

ソースコードはgit でクローンするので,git のインストールも行う.



# apt-get install git
# git clone git://git.qemu.org/qemu.git


次にビルドに必要なパッケージをインストールする.




# apt-get install make
# apt-get install gcc
# apt-get install pkg-config
# apt-get install libglib2.0-dev
# apt-get install libcap-dev
# apt-get install libattr1-dev
# apt-get install libpixman-1-dev


次に,ビルドの準備をする.

具体的には,ビルドしたいQEMU のバージョンのソースコードへチェックアウト,configure の実行である.

configure の実行はオプションが多くなりがちなので, setup.sh というスクリプトファイルにして実行してみた.



cd ~/qemu
git checkout v2.2.1
vi setup.sh




./configure --prefix=/opt/qemu-2.2.1 \
--target-list=x86_64-softmmu,x86_64-linux-user \
--enable-kvm \
--enable-vhost-net \




# sh setup.sh 2>&1 | tee -a _setup.log
# make -j8 2>&1 | tee -a _make.log
# make install 2>&1 | tee -a _make-install.log


最後に動作確認



# /opt/qemu-2.2.1/bin/qemu-system-x86_64 --version
QEMU emulator version 2.2.1, Copyright (c) 2003-2008 Fabrice Bellard




1.5 おわりに



書いてみて気づいたが,特別難しいところはなさそう.

ビルドに必要なパッケージをいつも忘れるくらい.






--
My Emacs Files At GitHub

2015年3月20日金曜日

Grub のブートメニューををシリアルポート経由で出力する


1 Grub のブートメニューををシリアルポート経由で出力する




1.1 動作環境




  • $ uname -a

    Linux debian 3.2.0-4-686-pae #1 SMP Debian 3.2.51-1 i686 GNU/Linux


  • $ sudo grub-install -v

    grub-install (GRUB) 1.99-27+deb7u2






1.2 設定方法



注) コマンドはあとで思い出しながら書いたところがあるので,コピペしても動かないかも



/etc/default/grub に以下を追記



GRUB_TERMINAL_INPUT='serial console'
GRUB_TERMINAL_OUTPUT='serial console'
GRUB_SERIAL_COMMAND='serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1'


grub-mkconfig で設定ファイルを更新.



# grub-mkconfig -o /boot/grub/grub.cfg


出力された /boot/grub/grub.cfg を確認する.




# grep 'serial' /boot/grub/grub.cfg
serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1
terminal_input serial console
terminal_output serial console


これで再起動すれば,GRUB のメニューがシリアルポート経由で出力される.






--
My Emacs Files At GitHub

2015年2月16日月曜日

iPXE でBitVisor をネットワークブートしよう


iPXE でBitVisor をネットワークブートしよう





はじめに



BitVisor のブートは,ローカルディレクトリにイメージを置いて,Grub から起動する方法を取ることが多いかなと思うが,これは開発時には結構面倒.

なぜなら,トライアンドエラー中にBitVisor のイメージを差し替えるためだけに,BitVisor なしで一度ブートしないといけないから.

ネットワークブートなら,サーバーにおいてあるBitVisor のイメージを差し替えればいいので,楽.

というわけで,ネットワークブートの方が楽だよ,と先輩に教わった方法をここに書く.




恐らく,ブートするのがBitVisorだからと言って特別なことはあまりない.

Linux をiPXE でネットワークブートするのと設定はほぼ同じだと思う(Linux はやったことないけど.)






環境




  • iPXEサーバーマシン: Raspberry Pi


  • OS: Raspbian






必要なもの




  1. BitVisor を起動マシンするマシン(BIOS の設定でネットワークブートを有効にできるもの)


  2. Raspberry Pi


  3. GNU make が動くx86マシン(1. のマシンでもいい)


    • iPXE のイメージとBitVisorのイメージを作るのに必要








設定方法




TFTP のインストール & 設定



使うのはtftpd-hpa.




$ sudo apt-get install tftpd-hpa
$ sudo vi /etc/default/tftpd-hpa
$ sudo service tftpd-hpa start


設定ファイルの変更は以下の通り




4c4
< TFTP_DIRECTORY="/srv/tftp"
---
> TFTP_DIRECTORY="/var/lib/tftpboot/"
6c6
< TFTP_OPTIONS="--secure"
---
> TFTP_OPTIONS="--secure -4"


上記設定では, /var/lib/tftpboot にiPXE のイメージを置く設定.

/var/lib/tftpboot がない状態でtftpd-hpa を起動しようとすると,失敗するらしい.



オプションに -4 をつけた理由.

= オプション追加せずにtftpd-hpa を起動しようとしたら,Raspbian でIPv6 が無効になってて動かなかったから.

ちょっと jot: obs600 で 再度 debian squeeze - その 1






DHCP サーバーのインストールと設定




$ sudo apt-get install isc-dhcp-server
$ sudo vi /etc/dhcp/dhcpd.conf #設定ファイルの編集
$ sudo service isc-dhcp-server


設定ファイルの変更(というより追記)は以下の通り




107a108,118
>
> subnet 192.168.0.0 netmask 255.255.255.0 {
> deny unknown-clients; # 下記でMACアドレスが登録されてない起動マシンは無視.
> host deep-desktop {
> hardware ethernet 00:60:6E:EB:5A:D8; # 起動マシンのMACアドレス.
> fixed-address 192.168.0.20; # BitVisorを起動するマシンに割り振るIPアドレス
> option host-name "deep-desktop"; # host *** の部分と同じ.
> next-server 192.168.0.30; # tftp サーバのIPアドレス.
> filename "bitvisor-boot.kpxe"; # ipxe イメージのファイル名
> }
> }


これで,MACアドレス 00:60:6E:EB:5A:D8 から要求が来たら, TFTP で /var/lib/tftpboot/bitvisor-boot.kpxe を送る.

bitvisor-boot.kpxe というのは,iPXE のイメージファイル.

このファイルの作り方は後述.





iPXE をイメージを作成



iPXE はイメージファイルをtftp で起動マシンに投げて,起動マシンで実行される.

なので,起動マシンがx86 ならx86向けにビルドする.

というわけで,以下のイメージファイルの作成の操作はx86マシンでやる.



$ git clone git://git.ipxe.org/ipxe.git
$ cd ipxe/src
$ vi bitvisor-boot.ipxe
$ cat bitvisor-boot.ipxe
#!ipxe
dhcp
boot bitvisor.elf
$ make -j4 bin/undionly.kpxe EMBED=bitvisor-boot.ipxe
# scp bin/undionly.kpxe deep@raspberrypi:~

bitvisor-boot.ipxe はiPXE でのブート処理を記述するファイル.

上記の設定で,サーバーの /var/lib/tftpboot/bitvisor.elf をブートする.

今回は書かないが,この設定をいじると,ブートするイメージを選択するメニューを出せたりもする.



make の結果, bin/undionly.kpxe が作成される.

これが,iPXE のイメージファイル.

このファイルをRaspberry Pi 上に置く.



以下は,Raspberry Pi上で操作.

TFTP のディレクトリにiPXE のイメージを置く(ついでにファイル名も変える)



$ sudo cp undionly.kpxe /var/lib/tftpboot/bitvisor-boot.kpxe





BitVisor のビルド



これもx86マシンでやる.



$ wget http://sourceforge.net/projects/bitvisor/files/latest/download?source=files -o bitvisor-1.4.tar.gz
$ tar -zxvf bitvisor-1.4.tar.gz
$ cd bitvisor-1.4/
$ tar -zxvf bitvisor-1.4.tar.gz
$ cd bitvisor-1.4/
$ history | tail -n3
$ make config # 動かすだけならデフォルト設定で大丈夫だと思う(多分)
$ make -j4
$ scp bitvisor.elf deep@raspberrypi:~


Raspberry Pi でbitvior.elf をTFTP のディレクトリに配置.



$ cp bitvisor.elf /var/lib/tftpboot/bitvisor.elf





いざブート…の前にBIOS 設定




  • ブートする前にBitVisor を起動するマシンのBIOS の設定を変更


    • ネットワークブートを有効にする(大抵どこかにあると思うので探そう,これなかったら諦めて別のマシンを用意して)




  • ブートの優先順位でネットワークブートを再優先にする


    • 優先順位の選択にネットワークブートを出すには,ネットワークブートを有効化してから一時Save&Exit しないといけないので注意.










おわりに



今回はRaspberry Pi をサーバにしてみたが,VMでもいいからx86 マシンを用意できるからそっちの方が多分よい.

なぜなら,iPXE のイメージを差し替えるときにいちいち別のマシンでビルドしないといけないから…



雑な説明になってしまったので,質問や突っ込みがあれば,是非お願いします.

(TFTP とかiPXE の仕組みはたぶん3割くらいしかわかってないので,仕組みとかは質問されても答えられないと思うけど…)






--
My Emacs Files At GitHub

2015年1月21日水曜日

org-mobile-pull/push を自動実行する設定


1.1 はじめに

org-mobile でTODO 管理とかすると,PC からの入力がストレスレスで良いのだけど,なにせ,いちいちコマンド叩いて同期するのが面倒.

というわけで,自動的にorg-mobile-pull とかorg-mobile-push とかするようにしてみた.

まだ,この設定で動かした時間短い(30分ぐらい)ので,どこかおかしなところを見逃してる可能性ある.

だから,あんまり鵜呑みにしないようにお願いします.
(追記: 後述のcapture ファイル監視でのpullは動いてないっぽい... 2015/01/21 09:12)





1.2 やること



  • Emacs が開いている間,アイドル時間が一定時間以上でpull してpush
  • org-mobile-capture-file が変更されたらpull (追記: 下記の設定では動いてないぽい)

    • capture ファイルは,org-mobile で同期取るときに使う差分を保持する一時ファイルだったかな? とにかく,他のデバイスとかで編集するとこのファイルが更新されるっぽい.
  • ファイルをセーブするときにはpush
  • ファイルを開くときにはpull




1.3 参考にしたコード



以下のリンクにあるサンプルコードを参考にした

FAQ · matburt/mobileorg-android Wiki

やってることは


  • org-mobile に登録したファイルを保存すると,その後アイドル時間30秒経過するとpush を実行
  • 24時間に一回の頻度でpush
  • Emacs 起動時にpull
  • org-mobile が更新されたらpull




1.4 やってみる




1.4.1 コード





(defvar org-mobile-sync-timer nil
  "Timer that `org-mobile-sync-timer' used to reschedule itself, or nil.")
;;pullしてpush する関数
(defun org-mobile-sync nil
  (interactive)
  (org-mobile-pull)
  (org-mobile-push))

;;idle 時間が60秒経過すると自動でpullしてpush
(defun org-mobile-set-sync-timer nil
  (interactive)
  (setq org-mobile-sync-timer (run-with-idle-timer 10 60 t 'org-mobile-sync)))  ;;修正 2015/01/21 09:04


;;自動pull, push  を無効にする(一応用意してみたが使うかどうか...)
(defun org-mobile-clear-sync-timer nil
  (interactive)
  (cancel-timer org-mobile-sync-timer))

;;↓要修正  (追記:2015/01/21 09:11)
;;指定されたファイルを指定された時間で監視,更新されたらorg-mobile-pull を実行
(defun install-monitor (file secs)
  (run-with-timer
   0 secs
   (lambda (f p)
     (unless (< p (second (time-since (elt (file-attributes f) 5))))
       (org-mobile-pull)))
   file secs))

;; capture ファイルを5秒ごとに監視,更新されたらorg-mobile-pull 実行
(install-monitor (file-truename
                  (concat
                   (file-name-as-directory org-mobile-directory)
                   org-mobile-capture-file))
                 5)

;; org-mobile に登録したファイルを保存するとpush
(add-hook 'after-save-hook
          (lambda ()
            (when (eq major-mode 'org-mode)
              (dolist (file (org-mobile-files-alist))
                (if (string= (file-truename (expand-file-name (car file)))
                             (file-truename (buffer-file-name)))
                    (org-mobile-push)))
              )))

;; org-mobile に登録したファイルを開くとpull
(add-hook 'find-file-hook
          (lambda ()
            (when (eq major-mode 'org-mode)
              (dolist (file (org-mobile-files-alist))
                (if (string= (file-truename (expand-file-name (car file)))
                             (file-truename (buffer-file-name)))
                    (org-mobile-pull)))
              )))
(org-mobile-set-sync-timer)  ;; ←追記 2015/01/21 09:00


  • ファイルをセーブするときpushするコードは,元のコードから遅延を省いただけ.
  • capture ファイル変更でpull するコードは元コードそのまま






1.5 おわりに



とりあえず,これでしばらく使ってみる.

もしかしたら何か問題あるかも知れない.

問題が見つかって,気が向いたらこの記事直します.(無責任)






--
My Emacs Files At GitHub

2015年1月19日月曜日

Emacs Lisp を少し勉強した


1 Emacs Lisp を少し勉強した   gblog



Emacs Lisp のコードを読むときに知らなくて困った(多分)基本的な所をメモ.

困ったときに調べたものだけメモしているので,網羅はしてない.




1.1 コンスセル



「CARスロットおよびCDRスロットと呼ばれる 2つのポインタから成るオブジェクトです。」らしい.

「各スロットは、任意のLispオブジェクトを指すことができます。」

ドット記法とかで表現する.

(A . B) と書くと,A がCARスロット,B がCDR スロット



参考ページ:

GNU Emacs Lispリファレンスマニュアル: Cons Cell Type







1.2 cons obj1 obj2



obj1 とobj2 から成るコンスセルを作る関数.

(cons 1 2) => (1 . 2)





1.3 リスト



リストはコンスセルの入れ子構造.

(1 2 3) と表示する.

この実態は,CARスロットが1, CDR スロットが(2 3) のコンスセル.

2と3 が要素のリスト(2 3) の実態は CARスロットが2, CDR スロットが(3) のコンスセル

3だけが要素のリスト(3) の実態は CAR スロットが3, CDR スロットが空リストの()

ドット表記だと(1 . (2 . (3 . ())

(cons 1 (2 3)) => (1 2 3)

(cons 1 ()) => (1)

また,空リスト() はnil とも書く

つまり,

(cons 1 nil) => (1)





1.4 関数car, cdr



コンスセルのCAR スロットを取り出すのがcar 関数,CDR スロットを取り出すのがcdr 関数.

リストに適用すると,以下のような感じ.

(car '(0 1 2)) => 0

(cdr '(0 1 2)) => (1 2)





1.5 シングルクォートは何?



car やcdr の例では'(0 1 2) とリストの前に「'」がついている.

これは,リストを評価しないようにするためのものらしい.

リストを評価するとは,リストのCAR スロット(第一要素)を関数名,第二引数以降を関数の引数として,計算するということ.

Lisp は(0 1 2) とだけ書いて実行すると,0 を関数名,1 を第一引数,2 を第二引数として計算しようとするらしい.

これをせずに,リストとしてそのまま置いておくために,シングルクォートをつける.

関数名や変数名の前に「'」をつけるのも同じ理由のようである.



参考ページ:

Programming in Emacs Lisp: List Processing



また,別の説明で,おそらくより厳密な説明が以下のリンクにある.

Emacs Lisp

これをちゃんと理解するためには,シンボルというものを理解しないといけないらしい.

また,シンボルなるものの構造については以下のリンクが詳しいと思う.

GNU Emacs Lispリファレンスマニュアル: シンボル

このあたりの理解は今後の課題.





1.6 let



ローカル変数を定義して,最後のリストを評価する.



(let (ローカル変数の定義のリスト) フォーム1 … フォームn)



ローカル変数の定義のリストは以下の構文

((ローカル変数名1 初期値1) ローカル変数名2 … (ローカル変数名n 初期値n))

(ローカル変数名1 初期値1) と言うリストは,ローカル変数名1 が初期値1 で初期化することを示す.

一方,ローカル変数2 とだけ書いていると,ローカル変数2 はnil で初期化する.



フォームとは,Emacs Lisp のコードと思えばいいのかな?

let 全体の戻り値は,最後のフォーム(フォームn) の戻り値になる.



例:

(let ((x 3)) x) => 3

(let ((x 3) (y 2)) x y) => 2 ;; 最後のフォームがlet の戻り値になる

(let ((x 3) (y 2)) (+ x y)) => 5

(let ((x 3) y (z 4)) (list x y z)) => (3 nil 4) ;; (list x y z) はx y z を要素とするリストを返す






1.7 スペシャルフォーム(special form) とマクロ



普通,リストの形式で書いて実行すると,全てのリストが評価される.

しかし,例外があるらしく,それがspecial form というらしい.

先のlet はその一つ.

(let ((x 3) (y 2)) (+ x y)) の(x 3) はリストだが,これを関数として評価していない(したらx なんて関数はないとエラーが出るはず)

一方,一番後ろの(+ x y) は評価されている.

このように,let の引数に関しては,一番後ろしか評価しない,ということになっているようである.

他にも,値を変数に代入するsetq などがそうらしい.



また,スペシャルフォームという言葉を調べていると,マクロという言葉もよく見かける.

マクロは,ユーザー定義のスペシャルフォームらしい.

参考ページ:

スペシャルフォームとマクロ - by shigemk2






1.8 dolist



実は,このブログの内容は,dolist のサンプルコードがわからなくて調べだしたのがきっかけ.

つまり,今回のブログはここがゴール.



dolist もlet のようにスペシャルフォーム.

構文は以下.

(dolist (ローカル変数 リスト [戻り値]) フォーム1 … フォームn)



リストの要素を一つずつローカル変数に代入してフォーム1 からフォームn の処理を繰り返す

python とかでいうforeach 的なやつ.

戻り値を設定しないと,dolist の戻り値はnil になる.

また,戻り値に未定義の変数は使えないので,予め定義する必要がある.

例:

(let ((sum 0)) ;; 戻り値に使う変数を定義

(dolist (x '(1 2 3) sum)

(setq sum (+ x sum)))) => 6






--
My Emacs Files At GitHub