!fn
的使用场景
根据给定 Org链接 指向的代码块构造 lambda.
在这里,我们稍微对 Org代码块 做个区分。当前,我们将其划分为三类:A类 是 elisp 代码片段;B类 是 elisp lambda; C类 是非 elisp 语言的代码块。
举例说明:
A类:
#+name: 2025-08-03-20-38
#+begin_src emacs-lisp :var args='("a" "b") :results silent
(apply #'concat args)
#+end_src
B类:
#+name: 2025-08-03-20-39
#+begin_src emacs-lisp :var args='("a" "b") :results silent
(lambda (&rest args)
(apply #'concat args))
#+end_src
C类:
#+name: 2025-08-03-20-40
#+begin_src python python :var args='("a" "b") :results silent
from functools import reduce
return reduce(lambda a,b:a+b, args)
#+end_src
#+name: 2025-08-03-20-41
#+begin_src C++ :var args='("a" "b") :includes '(<string> <iostream>)
std::string c = "";
for (int i = 0; i < sizeof(args)/sizeof(const char *); ++i)
{
c += std::string(args[i]);
}
std::cout << c;
#+end_src
借助 !fn
和几个工具函数,我们可以实现从 Org代码块 加载 lambda:
(!let (s+ r)
(!def s+
(!fn (&rest args)
(declare (!fn wrap-org-src-block))
[[2025-08-03-20-38]]))
(push (s+ "a" "b" "c") r)
(!def s+
(!fn (&rest args)
(declare (!fn from-org-src-block))
[[2025-08-03-20-39]]))
(push (s+ "a" "b" "c") r)
(!def s+
(!fn (&rest args)
(declare (!fn by-org-src-block))
[[2025-08-03-20-40]]))
(push (s+ "a" "b" "c") r)
(!def s+
(!fn (&rest args)
(declare (!fn by-org-src-block))
[[2025-08-03-20-41]]))
(push (s+ "a" "b" "c") r)
r)
;; => ("abc" "abc" "abc" "abc")
于是,我们可以很容易地从其他语言引入一些东西,比如,从 python 中引入矩阵乘法:
#+name: 2025-08-03-21-03
#+begin_src python :var a='((1) (2)) b='(3 4) :results silent
import numpy as np
a = np.mat(a)
b = np.mat(b)
c = a @ b
return c.tolist()
#+end_src
(!let ((m* (!fn (a b)
(declare (!fn by-org-src-block))
[[2025-08-03-21-03]])))
(m*
'((1 2 3)(4 5 6))
'((7 8 9 10)(11 12 13 14)(15 16 17 18))))
;; => ((74 80 86 92) (173 188 203 218))
实现
针对 A, B, C类,我们分别用如下的三个工具函数生成 lambda:
A类:
#+name: 2025-08-03-20-33
#+begin_src emacs-lisp :eval no :lexical t
(!def '!fn-wrap-org-src-block
(lambda (cdr)
(unless (fboundp 'org-noweb-expand-link)
(error
"!fn-wrap-org-src-block: %s"
"`org-noweb-expand-link' no found."))
(read
(format
"(lambda %S %s)"
(car cdr)
(org-noweb-expand-link
(format "%s" (car (last cdr))))))))
#+end_src
B类:
#+name: 2025-08-03-20-34
#+begin_src emacs-lisp :eval no :lexical t
(!def '!fn-from-org-src-block
(lambda (cdr)
(unless (fboundp 'org-noweb-expand-link)
(error
"!fn-from-org-src-block: %s"
"`org-noweb-expand-link' no found."))
(read
(org-noweb-expand-link
(format "%s" (car (last cdr)))))))
#+end_src
C类:
#+name: 2025-08-03-20-35
#+begin_src emacs-lisp :eval no :lexical t
(!def '!fn-by-org-src-block
(lambda (cdr)
(unless (fboundp 'org-exec)
(error
"!fn-by-org-src-block: %s"
"`org-exec' no found."))
`(lambda ,(car cdr)
(org-exec
,(car (last cdr)) nil
:eval "yes" :results "none"
,@(apply
#'append
(mapcar
(lambda (a)
(if (eq a '&rest) nil
`(',a `(identity ',,a))))
(car cdr)))))))
#+end_src