その他、gauche-mode の軽い設定など。

C-x C-e で評価すると、評価結果が minibuffer に表示されますが、末尾に改行が付いていて、不必要に minibuffer が拡がってしまうので、

;; C-x C-e 時に minibuffer が不必要に拡がるのを抑止。
(defadvice gauche-message (around expand-echoarea activate)
  "Not expand echo area, when the message was single-line."
  (let* ((fmt (ad-get-arg 0))
         (message (ad-get-args 1))
         (lines (with-temp-buffer
                  (insert (car message))
                  (count-lines-buffer))))
    (if (= 1 lines)
        (message fmt (car message))
      ad-do-it)))

としてそれを抑止してみました。
Emacs Lisp の M-: と同様の動作をして貰うために、

;; eval-expression.
(defun gauche-util-eval-expression (sexp)
  "Evaluate SEXP and print value in minibuffer."
  (interactive (list (read-string "Sexp: ")))
  (gauche-send-string-to-process (gauche-process) sexp))
(add-hook 'gauche-mode-hook
	  (lambda ()
		  (define-key (current-local-map)
        ;; [(control c) (meta :)] 'gauche-util-eval-expression)))
		    [(meta control :)] 'gauche-util-eval-expression)))

として M-C-: で minibuffer での評価もできる様にしてみました。

また、function-menu (fume) が動作してくれなくて悲しかったので、

;; setting for function-menu (fume).
(add-to-list 'fume-function-name-regexp-alist
    ;; Gauche
    '(gauche-mode . fume-function-name-regexp-scheme))
(add-to-list 'fume-find-function-name-method-alist
    ;; Gauche
    '(gauche-mode . fume-find-next-scheme-function))
(add-hook 'gauche-mode-hook
          (lambda ()
    (define-key (current-local-map) [(control c) r] 'fume-rescan-buffer)
    (define-key (current-local-map) [(meta p)] 'fume-goto-previous-function)
    (define-key (current-local-map) [(meta n)] 'fume-goto-next-function)))

と設定を追加しました。

こんな設定で、それなりに Emacs Lisp ライクな利用環境になってます。
あとは debugger かなあ。;; これは苦しいなあ。

しかし、gauche を使う設定とかで現実逃避してただけで、多値のこととか、

(use gauche.collection)
#<undef>
(fold2 (lambda (elt a b) (values (min elt a) (max elt b)))
       256 0
       (list 1 2 34 5 678 9 0 987 6 54 321))
0, 987

とかして、おおー、多値になるなるー、ってしただけです。
全く何やってんだか……。

しかし、やはり Gauche 専用のモードがあった方が良いでしょうね。

そこで、http://www.shiro.dreamhost.com/scheme/wiliki/wiliki.cgi?Gauche%3aEditingWithEmacs で幾つか紹介されている Majar Mode から、http://www.hh.iij4u.or.jp/~nishio/gauche-mode/ を使ってみることにしました。
この時点で最新の snap shot だった gauche-mode-20060110.tar.bz2 を download して、README の通りに make してみたんですが、

*** ERROR: string required, but got #f
Stack Trace:
_______________________________________
  0  (string->list format-string)
        At line 37 of "/usr/share/gauche/0.8.3/lib/srfi-29/format.scm"
  1  (format #f ";; exclude modules:\t~s\n" (cdr argv))
        At line 50 of "./make-sym.scm"
  2  (print-comments)
        At line 112 of "./make-sym.scm"
make: *** [BSD/gauche-module-symbol.dat] エラー 70

となってダメでした。
gauche で error を吐いてるので、XEmacs とは関係無さそうです。
README を見てみると、gauche-mode は最終更新が 2005-12-08 で、しかも `cleanup make-sym.scm' となっていて、error を起こした script そのものです。
これは gauche を最新に上げないと無理な気が。
一応、一世代古い gauche-mode も試してみましたが同じでした。

make -n してみると、いきなり gosh make-sym.scm とかして error になっているので、これを単独で実行してみても全く同じ結果に。
つまり、このスクリプトが gosh 上で正しく動作できていないということなので、gauche の構築した環境がマッチしてないという可能性が高いことになります。

となればやはり最新を試してみるべきなので、Gauche-0.8.6.tgz (2006/2/20時点の最新。現在は 0.8.7) を download して展開。
make && make test で問題無し。そのまま sudo make install として最新版のインストール完了。

$ gosh -V
Gauche scheme interpreter, version 0.8.6 [utf-8,pthreads]

となりました。

そして gauche-mode を make し直してみると、今度は成功しました。
しかし、私は普段、C-h を `backward-delete-char' に bind しているのですが、gauche-mode では `apropos' に bind してしまうので、bind し直すことにしました。

しかし、この gauche-mode、モードの実行時に gosh の process を生成して、その process にインタラクティブに評価させてるんですね。
これまで PerlRuby をその様な発想で利用したことはありませんでした。考えてみれば Perl/Ruby の debbugger や shell buffer (shell-mode) と同じなんですが、スクリプトの編集モードとそれを括りつける発想がありませんでしたねえ。
Emacs Lisp では当然の様にやってた訳ですが、Emacs Lisp だから特別に感じていた (そもそも EmacsEmacs Lispインタプリタな訳ですし) ということでしょうね。
Scheme ということで、Emacs Lisp と同様に利用したいという強い欲求から来たものでしょうか。素晴しいです。

それならばついでに、という事で、Emacs の scratch buffer と同様のものを作ってしまうことにしました。

;; Create scratch buffer for Gauche.
(defconst gauche-util-help-message
  ";; If you want to exit this buffer, type C-c C-q.")
(defun gauche-util-create-scratch-buffer ()
  "Create scratch buffer for Gauche."
  (interactive)
  (let ((sbuf (generate-new-buffer "*gauche scratch*")))
    (set-buffer sbuf)
    (insert
     (concat initial-scratch-message
             gauche-util-help-message
             "\n\n"))
    (save-excursion
      (goto-char (point-min))
      (let ((end (re-search-forward "lisp" nil nil nil))
            (start (match-beginning 0)))
        (replace-match "gauche" nil nil)))
    (set-buffer-modified-p nil)
    (switch-to-buffer sbuf)
    (gauche-mode)
    (save-excursion
      (goto-char (point-min))
      (let ((end (re-search-forward "for gauche evaluation" nil nil nil))
            (start (match-beginning 0)))
        (add-text-properties start end '(face font-lock-reference-face))))))
(defun gauche-util-kill-scratch-buffer ()
  "Kill scratch buffer for Gauche."
  (interactive)
  (progn
    (gauche-kill-gauche-process)
    (kill-buffer (current-buffer))))

(global-set-key [(control q) G] 'gauche-util-create-scratch-buffer)
(add-hook 'gauche-mode-hook
	  (lambda () (define-key (current-local-map)
                 [(control c) q] 'gauche-util-kill-scratch-buffer)))

このコード、scratch buffer の生成時に挿入されている文字列のうち、

for Lisp evaluation.

となっているところを、

for Gauche evaluation.

と書き変えて更に強調する処理を加えるため*だけ*に、余計なコードが歪な構成で入ってますが、それはご愛嬌ということで。あと、そもそも gauche-mode が XEmacs でしか動作しないので、このコードも XEmacs 以外のことは何も考えてません。
でも、そろそろ本当に FSF Emacs に移行しなきゃと思ってるんですけど、どんどん自分で自分の首を絞めてますね。

実は Gauche をインストールしていました。

もう二ヶ月前になりますが、多値関連のエントリで shiro さんに指摘を受けたことから、やっぱり Scheme の環境をちゃんと持っておかなくてはと思い、自分の環境に Gauche を install し、遊んでみることにしてました。
その後、全く構築した環境にも Web にも接触できない状況に陥ってしまっていたのでそのまま放置になってしまっていますが……
取り敢えず、その時にやっておいたことを記録しておこうかと。

インストールした環境は、

coLinux (Debian/Sarge) on Windows XP
$ uname -a
Linux XXXXXXX 2.6.10-co-0.6.2 #5 Sat Feb 5 10:19:16 IST 2005 i686 GNU/Linux

です。
Debian/Sarge で aptitude を使って install できるのは 0.8.3-3 でしたが、2006/2/17 時点の gauche の最新版は 0.8.6 でした。

;; 今確認してみたら、
;;
;; 2006/4/12
;; Gauche 0.8.7: 大きなメンテナンスリリース
;;
;; なんてことになってる……

この Debian/Sarge では、基本的には aptitude で package を管理する方針としているので、取り敢えずは aptitude で install しておくことにしました。
;; 今のところ make install しているのは Trac と CUnit だけ。
問題ある様なら、その時には source から install し直すということで。

aptitude で、gauche を選択して install すると、

[インストール] gauche
[インストール] gauche-doc
[インストール] gauche-gdbm
[インストール] gauche-gl
[インストール] gauche-gtk
[依存関係によりインストール] freeglut3
[依存関係によりインストール] libglut3
[依存関係によりインストール] libgtkglext1
[依存関係によりインストール] slib
[維持] gpdf

が install されました。

$ whereis gosh
gosh: /usr/bin/gosh /usr/share/man/man1/gosh.1.gz
$ gosh -V
Gauche scheme interpreter, version 0.8.3 [utf-8,pthreads]

と gosh (0.8.3) が install されていることが確認できました。

但し、この時点で、

といった問題が。
多分、何れも XEmacs なせいかと。

gauche-refj.info の方は文字コードの問題かとも思ったのですが、GNU Info では読めるのでそうではない様です。
追ってみると、どうやら gauche-refj-info の Tag Table に書かれた offset がおかしい模様。(XEmacs の Info だと正しく解釈できないのかな?)
取り敢えず、各 node の offset を書き換えるコードを書いて実行。超適当ですが何とか読める様になりました。

run-scheme の方は、XEmacs の xscheme.el が、scheme インタプリタを起動する時のコマンドラインに、無条件に `-emacs' を付加する様にハードコードされていました。
xscheme.el に手を入れたくなかったので、scheme-program-name には wrapper となる sh script を登録し、その wrapper で `-emacs' を削ることにしました。
`-emacs' は、コマンドライン引数の先頭に指定される様になっていたので、その wrapper は、

#!/bin/sh
shift
gosh $*

としただけです。

これで動くには動いたんですが、

  • 基本的に xscheme-mode では、FSF Emacsscheme-mode と異なり、`gosh>' 等のプロンプトは表示しない様で、ちょっと判り辛いので、`-i' を渡す様に設定。
  • font-lock を有効に。
  • C-x C-e (advertised-xscheme-send-previous-expression) で評価時に、評価結果がポイントの直後に挿入されるのが気に入らないので、`?\n' を挿入する様にアドバイス
  • 上のせいで M-{n,p} (xscheme-yank-{pop,push}) 時に改行まで履歴として保持されてしまうので、履歴復元時には delete-char する様にアドバイス

ということで、

;; C-x C-e したら改行してから評価結果を表示。
(defadvice xscheme-send-string
  (before exp-newline activate)
  (insert ?\n))
;; xscheme-yank-{push,pop} では改行を削る。
(defadvice xscheme-yank-push (after shave-newline activate)
  (backward-delete-char))
(defadvice xscheme-yank-pop (after shave-newline-pop activate)
  (backward-delete-char))
;; xscheme-mode で font-lock.
(add-hook 'scheme-mode-hook #'(lambda () (font-lock-mode)))

として、取り敢えずは好みに使える様になりました。

ia64 での Ruby の挙動が……

で、migemo をインストールすべく、bsearch.rb, romkan.rb を home ディレクトリに配置し、migemo の configure && make... と、make test で引っ掛かりました。
どうやら migemo の辞書を作る事ができません。
色々と調べてみると、どうやら、

[ruby-dev:24748] "\000\000\000\001".unpack("N") returns [0]
■ Alpha で unpack がバグる

この辺の問題らしく、pack した offset が unpack で元に戻っていない様です。

この環境の Ruby のバージョンは、

$ ruby --version
ruby 1.8.1 (2003-12-25) [ia64-linux-gnu]

で、明確に上の url の事象と同じかどうかは確認していませんが、

irb(main):020:0> [2, 55, 2**17 + 55, 2**18 + 10].pack("N*").unpack("N*")
=> [8589934592, 236223201280, 563186176622592, 1125942856515584]

と、正しく戻らないのは間違いありません。

make では辞書を作ることができず test にも通らない訳ですが、実際の migemo の処理自体にも影響するので、辞書だけ何とかする訳にも行きません。

当然、ruby を update すれば良いのですが、いつもと違って今回の開発環境は借り物なので、管理グループにお伺いを立て、お願いしなければなりません。
現在の状況ではスムーズな update は望めなさそうなので、ここは script 側で暫定対処してみました。

非常に ad-hoc な hack ですが、

irb(main):019:0> [2, 55, 2**17+55, 2**18 + 10].pack("N*").unpack("n*")
=> [0, 2, 0, 55, 2, 55, 4, 10]

と、short (2バイト) 単位に unpack すれば情報を失なうことはない様なので、これを使って何とかしてみました。
つまり 2バイト単位に unpack して、上位桁部分の桁を合わせた上で合算してやれば元の値を得られるということですね。

ということで、

def make_unpack_corrector(constant)
  x = nil
  result = nil
  c = constant
  return Proc.new { |elem|
    if x then result = x + elem; x = nil; result else x = elem * c; nil end }
end

なんてクロージャもどきを返すメソッドを用意し、

corrector = make_unpack_corrector(2**16)
@index = File.new(filename + ".idx").read.unpack("n*").map { |e|
  corrector.call(e) }.select { |e| e }

という具合に unpack("n*") した配列要素に map でクロージャもどきを適用した上で、select で値が存在する要素だけに絞り込みます。
何だか回りくどい方法になってしまいましたが、Ruby 的にはもっと上手い方法があるんじゃないでしょうか?

という訳で、手元の migemo に、

$ diff -u migemo-dict.rb.org migemo-dict.rb
--- migemo-dict.rb.org	2002-10-22 14:38:14.000000000 +0900
+++ migemo-dict.rb	2006-04-10 14:34:20.000000000 +0900
@@ -13,6 +13,14 @@
 
 require 'bsearch'
 
+def make_unpack_corrector(constant)
+  x = nil
+  result = nil
+  c = constant
+  return Proc.new { |elem|
+    if x then result = x + elem; x = nil; result else x = elem * c; nil end }
+end
+
 class String
   def prefix_match (string)
     self[0, string.length] <=> string
@@ -56,7 +64,9 @@
 class MigemoStaticDict < MigemoDict
   def initialize (filename)
     super(filename)
-    @index = File.new(filename + ".idx").read.unpack "N*"
+    corrector = make_unpack_corrector(2**16)
+    @index = File.new(filename + ".idx").read.unpack("n*").map { |e|
+      corrector.call(e) }.select { |e| e }
   end
 
   private
@@ -106,14 +116,23 @@
 class MigemoDictCache
   def initialize (filename)
     @dict  = File.new(filename)
-    @index = File.new(filename + ".idx").read.unpack "N*"
+    corrector = make_unpack_corrector(2**16)
+    @index = File.new(filename + ".idx").read.unpack("n*").map { |e|
+      corrector.call(e) }.select { |e| e }
   end
 
   def decompose (idx)
     @dict.seek(idx)
-    keylen = @dict.read(4).unpack("N").first
+
+    corrector = make_unpack_corrector(2**16)
+    keylen = @dict.read(4).unpack("n*").map { |e|
+      corrector.call(e) }.select { |e| e}.first
+
     key = @dict.read(keylen).unpack("a*").first
-    datalen  = @dict.read(4).unpack("N").first
+
+    datalen  = @dict.read(4).unpack("n*").map { |e|
+      corrector.call(e) }.select { |e| e }.first
+
     data     = Marshal.load(@dict.read(datalen))
     return key, data
   end

ってな感じの修正をして何とか使える様になりました。

decompose() 内の、

keylen = @dict.read(4).unpack("N").first

などは、read で読むバイト数も決め打ちの処理なので、

keylen = @dict.read(4).unpack("n*").last

とかでも全然問題無い (値が short の範囲に収まる限り) んですが、折角、クロージャもどきを作ったので適用しています。

それにつけても、map して select ってのは美しくないですね。

Migemo が使えないのは困ります。

新しい開発環境を与えられたので、好みの環境を設定してみました。
一応、shell と ssh の環境を整え、svn の client も動作可能な状態にしてから、いそいそと Emacs/XEmacs の環境を設定していました。

私は普段 XEmacs を利用しているので、ここでも取り敢えずは XEmacs の設定を。
FSF Emacs への移行は、もう随分と前 (emacs-w3m などで活躍されている山岡さんが XEmacs に愛想を尽かしてしまった頃) から考えてはいるものの、移行の手間がネックとなり、遅々として進んでいないのです。
今回の様に、新しい環境で XEmacs を利用し出してしまうと、またしても移行の手間が増えるので、こんなことではいつまで経っても移行できないんでしょうねえ。

XEmacs の初期化ファイル群を svnリポジトリから co し、サクサクと設定をして行きます。取り敢えず、動作しないライブラリは置いといて、先ずは問題無く XEmacs が起動されるまでは動作しない設定を除外して行きます。
取り敢えず、

を除外して起動に成功しました。
また、apel, flim も少しおかしいのですが、mu-cite も含め wanderlust を利用しない限り影響は無いと思うので、これらは後回しです。
emacs-w3m も取り敢えずは開発環境から利用できなくても構わないので後回し。

で、絶対必須と思っているのが migemo です。
私の場合、これなしではあらゆる面で生産性に影響が出るでしょう。

多値関数の議論

■[雑記/備忘]なぜ、多値関数は人気がないのだろう

次々に興味深い話題が。もう本当に片付けなきゃならないことが山程あるだろうって。

最初にこのエントリを読みながら思ったこととしては、

  • 多値を返す事ができる言語としては、Perl, Ruby, Python があるか。って、最近のスクリプト言語は大抵返せるってことか。
  • Emacs Lisp はできないけど、Common Lisp はできるみたいだ。あと Scheme も。
  • そもそも多重代入ができないと意味が無いのか。
  • 戻り値として返すことはそれ程面倒なことではなさそうだけど、多値を返す関数を考えたときに、その関数の使われる局面を考えると、あまり人間の思考に似わない気がするな。

といったところでした。

で、もう少し考えてみて、先ず最初の一つ目がおかしいことに気付きました。
そもそも Perl は引数にも戻り値にもリストが使えるというだけの話で、何れの場合でも引数は一つですね。文脈から、スカラーかリストかを判断しているに過ぎなかった筈。
更に多重代入も厳密には無い筈 (ですよね?) で、リストに放り込むということができるだけです。ここでもスカラとリストを文脈に応じて判断しています。

また、Ruby は values() によって多値が表現できるかの様に見せかけているだけですから、厳密には多値を返しているとは言えないのかもしれません。
;; まつもとさん自ら values() のことを「にせものの多値」と表現されていたり。
;; Ruby2 では多値を言語仕様に含む予定がある様ですが。
Python ではどうなんでしょう。values() を使うのは同じ様ですけど。

この辺り、何れもやってることは、引数の参照渡しの機構 (call-by-reference や call-by-name) を持たない C 言語の関数呼び出し機構に於ける擬似的な参照渡し (call-by-value でポインタを渡す) と似ていると思います。
一種のシンタックスシュガーでしょうか。

で、皆さんのコメントなどを拝見しながら少し調べてみると、プリミティブに多値に対応している Common LispScheme でも、実際に利用する際にはリストや配列を経由する必要がある様です。
※ 2006.02.13 shiro さんの指摘を受け削除しました。コメントを参照して下さい。
※ 以降の記述に影響がありそうなので、併せて変更が入るかもしれません。今のところはここを削除したのみです。

こうなると、厳密な戻り値の多値 (レジスタを複数使うとかスタックに複数の要素を積むとか) って必要なのかという気がしてきました。

そもそもの檜山さんの問い掛けでは、この様な意味で、引数との対称性という観点から、何故、戻り値には多値が無いのか? と考えておられると解釈しています。
その意味では、やはり上に挙げた幾つかの言語では、SchemeCommon Lisp でしか該当するものは無さそうですが、それでも結果的にはリストや配列を介してアクセスするのであれば、概念としては PerlRuby の仕組みも同じだという気もします。

そしてリストなどのデータ構造を介して複数の要素を返すことで用が足りるのであれば、Emacs Lisp でも C 言語でも同じことは可能な訳で、結局、殆どの言語で、サブルーチンの戻り値として複数の値 (多値) を返すことは可能と言えてしまう様に思えます。
あとはそれがスマートかとか、簡単かとか、そういう問題でしょうか。

必要かどうかということとはまた別に、対称性という意味では、ある関数の引数に別の関数の評価結果を指定する場合を考えてみます。
引数に指定された関数が多値を返す関数であると、それは複数の引数を表わすことになりますが、これが非常に悩ましいのだと思います。
実装云々よりも、セマンティクスとしてで、その様な構造が汎用的に可能なのは、

  • コレクションを返す関数
  • ある種の構造を返す関数

しか無い様に思います。
例えば例として挙げられている割り算の商と余りを返す多値関数ですが、これを他の関数の引数として指定することは無いだろうな、と。この様な関数は結果として代入式の右辺でしか使われず、とても使い辛い関数になってしまうのではないでしょうか。(とても手続き的な感じですね)
そういうものと割り切ってしまえば良いのかもしれませんけど。

私もなんだか考えるのがめんどくさくなってしまいました。(檜山さんの真似です。実は時間が無いのと頭がオーバーヒートしてるということです)
もう少し考えてみたいとは思うのですが、今日はここまででしょうか。

[追記]
最初の投稿でタイトルを失念してしまったために、トラックバックがちょっと変になってしまいました。
すみません、檜山さん。

Perldoc を引きたい。(続き)

■[Emacs][perl] Perlモジュールのソースを簡単に開く機能の話の続き

と、先日のエントリで Trackback を送った id:higepon さんに取り上げて貰っておきながら、全く反応できませんでした。申し訳ないことです。
このところ、公私共に色々とあって (と言っても圧倒的に `私' の方が上なのですが)、すっかり忙殺されてしまっていますもので。

一応、少し前には言及されていることに気付いており、また、

cperl-modeに取り込んでもらうようにするってのはとても良いアイデアですね。

やり方は二つあると思っていて

  • id:tokuhiromさんや、id:tenyさんの実装のほうが優れているのでどちらかにお任せする
  • id:higeponが言いだしっぺなので、自分のLispにお二人の成果を入れてから cperl-mode作者に突入する。

のどちらがよいでしょうか。>コメント・トラックバック下さった皆様。

と振られていたことにも気付いたんですが、如何せん反応する余裕がありませんでした。
ごめんなさい。

higepon さんには優れている実装の仲間に私のものも含めて頂いてますが、私のアプローチは単なる小技ですし、且つ、完全に Man パッケージにおんぶにだっこなので、cperl-mode の様なパッケージからすると取り込みたくないだろうという気持ちもありました。
この様なコマンドの出力を取り込む類いのパッケージについて、コマンドやオプションスイッチを変更して上手いことやるってのは色んな人がやってる小技系のアプローチなので Emacsen のカスタマイズの一環とも言えますし。

ところが、cperl-mode のコードを覗いてみたところ、cperl-perldoc() ではしっかりと、

  (require 'man)

してました。
というか、やってることは殆ど一緒ですね、私のコードと。
但し、cperl-mode では perl で使われるシンボルのリストを抱えているので、それらと対象の単語を照合して関数名か否かを判別し、関数名なら `-f' を指定して perldoc を実行する様になっています。
そのため、私が示したコードの様な無理矢理な部分 (コンテンツが存在するまでスイッチを変更しながら perldoc コマンドを実行する) はなくてスッキリしたものです。

とはいえ、私のコードでは、perldoc の出力となったモジュールのソースを manual-mode で開くことになりますが、id:higepon さんや id:tokuhirom さんのコードの場合、モジュールのソースは cperl-mode で開かれますので、より実用的かと思います。
;; それでも find-file よりも find-file-read-only とかの方が良さげな気はしますが。
;; でも、通常、モジュールは一般ユーザから更新可能とは思えないので、結果的に read only になるから良いという気もしますね。

という訳で、遅蒔きながら、

■[Perl][Emacs] cperl-find-module

id:tokuhiromさんがブラッシュアップしてくれた、cperl-find-moduleをcperl-modeのメンテナー( Ilya Zakharevich )にメールしたところ、User unknownで返って来てしまいました。

cperl-find-moduleとして配布したほうが良いのかな。

という動きで宜しいかと思っています。

が、User unknown ですか。これって最新の cperl-mode.el に書かれたアドレスですか?

wget http://math.berkeley.edu/~ilya/software/emacs/cperl-mode.el.5.15.bz2

として download した、

;; $Id: cperl-mode.el,v 5.15 2006/01/28 20:01:45 vera Exp vera $

では、cperl@ilyaz.org となっていますが、これが unknown だったのでしょうか。

Perldoc を引きたい。

;; もう今さら、な感じも無きにしもあらずですが。
少し前のはてなの質問に、

Emacs ユーザーの方に質問です。これは便利! と思える elisp プログラムを教えてください。

というのがあって、私も回答 (過去、答えてみようと思ったはてなの質問が二件あるのですが、何れも id:naoya さんの質問でした。別に他意は無いのですが、どうも私の深層意識の何かに触れるのでしょうか?) なんぞしてみた訳なんですが、私の回答への naoya さんのコメントに、

カーソル位置のパッケージの perldoc をワンアクションで開ける lisp とかないかなあ。

とあったので、少しゴニョってみた訳です。
しかし、色々あって放置状態になっていたのですが、

■[Perl][Emacs] Perlモジュール名にカーソルを合わせて M-.するとソースを開く2

といった一連の id:higepon さんのエントリを見付けてしまったので、ちょっと掘り起こしてみました。

higepon さんとは異なり、私の場合は先の質問の回答にもあった様に、Emacs 標準パッケージである `Man' を利用して perldoc を引いてみようというものです。

(defun perldoc-by-module (word)
  "Run `perldoc' with `Man' package, by module."
  (interactive "sperldoc: ")
  (let ((Manual-program "perldoc")      ; for XEmacs
        (Manual-switches '(""))         ; for XEmacs
        (manual-program "perldoc"))     ; for Emacs
    (manual-entry word)
    (and (featurep 'xemacs) (rename-buffer (concat "Perldoc: " word)))))

(defun perldoc-by-module-at-point ()
  "Run `perldoc' with `Man' package, by the word around point."
  (interactive)
  (let ((word (thing-at-point 'word)))
    (perldoc-by-module word)))

これで Man パッケージを利用して、ポインタ付近の単語を Perldoc で引くことができます。
;; Emacs/XEmacs で動作します。
;; もしも Man 関連の関数が使えなかったら (require 'man) して下さい。
thing-at-point は、cperl を利用しているなら、cperl-word-at-point とかにした方が良いかもしれません。

しかし、これだけだと、引きたい単語によって、わざわざモジュールなのか関数なのかで実行するコマンドを変えないとならないので、ちょっと面倒ですね。
そこで、少し長くなりますが、

(defun perldoc (&optional module word)
  "Run `perldoc' with `Man' package."
  (interactive "P")
  (require 'man)
  (let* ((Manual-program "perldoc")     ; for XEmacs
         (default (thing-at-point 'word))
         (word (or word (read-string "perldoc: " default 'perldoc-history))))
    (let ((switches 
           (if (featurep 'xemacs)
               (if module '("-m") '("" "-f" "-q")) ; for XEmacs
             (if module '("perldoc -m")       ; for Emacs
               '("perldoc" "perldoc -f" "perldoc -q")))))
      (if (setq ret (catch 'match
            (mapcar (lambda (sw)
                      (let ((Manual-switches (list sw))
                            (manual-program sw))
                        (if (featurep 'xemacs)
                            ;; XEmacs
                            (when (condition-case nil (manual-entry word)
                                    (error nil))
                              (throw 'match sw))
                          ;; Emacs
                          (Man-getpage-in-background word)
                          (sit-for 1)
                          (when (get-buffer (concat "*Man " word "*"))
                            (throw 'match sw))))) switches) nil))
          (if (featurep 'xemacs)
              ;; Emacs のときは rename するとまずいことになるので。
              (let ((name (concat "*Perldoc"
                                  (when (string-match "-" ret)
                                    (concat " " ret)) ": " word "*")))
                (if (get-buffer name)
                    ;; 既にあるなら、新しいのは消してそれを使う。
                    (progn
                      (kill-buffer (current-buffer))
                      (switch-to-buffer name))
                  ;; `*Man XXX*' -> `*Perldoc: XXX*'
                  (rename-buffer name))))
        (message "perldoc %s not found." word)))))

(defun perldoc-at-point (&optional module)
  "Run `perldoc' with `Man' package, by the word around point."
  (interactive "P")
  (let ((word (thing-at-point 'word)))
    (perldoc module word)))

という具合にすると、その辺りを意識しなくて良くなります。

これもポインタ付近の単語を perldoc で引く訳ですが、

  1. オプション無し (perldoc)
  2. 関数検索指定 (perldoc -f)
  3. FAQ 検索指定 (perldoc -q) ;; ここまでは不要かな。

の順に対象の単語が見付かるまで検索し、最終的に何も見付からなければ error となります。
また、前置引数を指定すると強制的に perldoc -m し、対象単語をモジュール名と看倣してそのモジュールのソースを表示します。

これも Emacs/XEmacs で動作しますが、私は XEmacs を常用しており、FSF Emacs は今の Debian Sarge の環境では、先の質問の回答時に初めて起動したという具合です。
そのため、Emacs での利用には問題があるかもしれません。
;; XEmacs でも問題が無いと保証できる訳ではありませんが。

また、FSF Emacs の Man パッケージは、バックグラウンドで非同期プロセスとして実行する様な仕組みになっており、該当コンテンツが無かったときには、非同期プロセスの番兵で error を throw する様に作られているため、condition-case で error を捕捉できない様です。
そのため、XEmacs の様に単純にコマンドを順次実行することができず、無理矢理な判断を噛ませていますので、動作がモタつくということもあります。
Man-getpage-in-background の直後に sit-for を入れていることもその一つですが、ここで待つ秒数は環境に依存すると思いますので、それぞれの環境で調整して貰った方が良いと思います。

Man パッケージ自身を変更してしまう手もありますが、既存のパッケージを使う際にはできるだけオリジナルのままにしておきたいのでこんな具合になっています。advice では解決できそうにないので。
ここは FSF Emacs に詳しい方に何とかして頂きたいところです。

あと、調子に乗って XEmacs では作ったバッファの名前も rename していますが、これは必要なものではないです。何となく *Man: Perlfunc(1)* とかになっているのが気に入らないというだけの話ですので。
そのせいで、本来なら Man パッケージが、既存のバッファの場合にはそれを表示する動作をしてくれるのにも係わらず、同じ単語を引いてしまったときは、もう一度バッファを作ってから既存のバッファを消すという、本末転倒な動作になってしまっています。
;; やっぱりこれは外した方が良さそうですね。

本来は Emacs 標準の Man パッケージを利用して簡単に実現するところがミソだったのですが、余計なことをし出したために、何だか長く面倒な elisp になってしまいました。
それでも perldoc の option を変更しながら順次実行して、該当するコンテンツが見付かるまでやるということに意義が感じられる様なら使えるかもしれませんね。

でもね、実は衝撃的なことに、

  M-x cperl-perldoc-at-point (C-c C-h P)

なんてコマンドも実はあったんですよ……

;; 実は皆さん、知っててやってるとか?