emacs将org文档输出位html,emacs-document/文学编程简介.org at master · lujun9972/emacs-document · GitHub...

文学编程简介

欢迎来到文学编程的世界! 本文会简单介绍一下如何使用org-mode进行文学编程

本文的内容来源于PDX Emacs Hackers举办的研讨会, 但是考虑到很少人能够现场参加该研讨会,因此我决定把它写成一片指南. 我假设你已经有了一定的Emacs基础 并且对org-mode也比较熟悉 ,至少知道如何导出文档.

众所周知,Org的功能太多了, 它具有在一个文档中编写,执行和连接代码块的能力,这种能力十分强大, 要把这些功能都写成文档是件让人觉的恐怖的任务. 我希望这份指南能开个好头, 若你发现我遗漏了什么,请告之.

警告 :下面的例子都写得很蹩脚.

必要条件

本文基于org-mode v8.2.10(或更新). 由于Emacs已经内置了org, 你需要通过 C-h v org-version 来看所使用的org是否符合要求,以保障本指南例子能够正确运行(大多数的例子应该没问题,但是也不好说…).

若你的org版本太旧,请运行下面这段代码:

(when (>= emacs-major-version 24)

(require 'package)

(add-to-list 'package-archives

'("org" . "http://orgmode.org/elpa/")

t)

(package-initialize)

(package-refresh-contents)

(package-install "org"))

然后应该就装上了最新的org了. 此外你还需要安装 org-plus-contrib.

在这篇指南中,我会向你展示多种语言的接口,但这也意味着你需要在本地安装大量的解释器,你至少需要安装下面几个解释器:

Shell (很抱歉,对于Windows用户来说,这一点恐怕比较难实现)

Python

Ruby

当然,你是个聪明人,把这些例子修改成你所使用的语言应该是很容易的.

背景知识

在我们开始学习使用org-mode 进行文学编程之前,我先简单介绍一下文学编程吧.

为什么使用文学编程?

文学编程最初是由Donald Knuth于20世纪80年代提出来的,旨在加强团队成员中的交流. 如Donald Knuth 所说过的

让我们改变针对程序结构的传统看法吧. 不要把我们的任务看成是向计算机描述怎么去作,而是向其他人解释我们想让计算机做什么.

文学编程希望能做到写得程序适于人阅读,文档描述能遵循问题逻辑的顺序,同时不受制于编程语言的缺陷. 下面是一份文学编程的例子,对人来说,这是份可阅读的文档,同时它还是源代码文件:

21ae666090c8df49a69e748956b38e64.png

其背后的思想是反转注释与代码之间的关系,以前是代码中点缀着些注释,现在则是在文档中内嵌一些代码.

由于从文学编程文档中抽取出来的源代码与直接编写的源代码无异,因此这一做法并不需要现有的编程环境做出什么改变.(although it is used, to various degrees, in niche circles).

为什么使用Org?

Knuth最开始认为文学编程只需要使用最简单功能的编辑器就行, 因为他只是写了noweb程序来创建文学编程文档并从中抽取源代码.

依我的观点,文学编程必须依赖编辑器的帮助,iPython’s notebook就是个例子. 但是我认为文档内容应该以可读文本的方式存储,而不是存储为JSON格式的文件. 就像 Carsten Dominik 说过的:

在第三个千禧年,使用文本文件是否还有意义? 文本文件是唯一真正可以说是可移植的文件格式. 用文本文件保存的数据永远不会丢失.

因此若你想进行文学编程,org-mode是个很好的选择,更何况我们本来就已经在用org-mode作许多事情了, 对吧?

使用Org有什么优势?

使用org-mode进行文学编程有如下几点优势:

代码更易理解

方便团队讨论问题

针对复杂状况更容易思考的清晰一点

学习新库和API时,可以一边实验一边记笔记

可以同时使用多种语言,每种语言只完成自己擅长的任务(例如可以使用通用语言来查询,维护数据库)

org具有组织化的能力,它提供了类似Agenda和task这类的工具

警告

1980年的软件世界与今天的情况相比,已经发生了翻天覆地的改变. 现在的工程师们连接的更紧密了,且常常以团队的形式进行工作. 虽然如此,但各个工程师所使用的工具还是各不相同的. 即使你的团队并不使用Emacs, 你依然会觉得org-mode的方法很有用处.

首先,org-mode很适合用于设计复杂算法,当你有什么灵感的时候,可以立即写下最终的代码(你所写下的记录会成为代码中的注释), 当你毫无进展时,你可以直接把记录发给团队中的其他人敬候回应.

其次,org-mode文件可以被认为是一个repl环境,因为其中的每个代码块都能被分别执行,并且代码块的执行结果还能传递给其他的代码块… but I’m getting a head of myself.

导出文档

使用org-mode的主要原因是可以将org文件导出成HTML,电子邮件信息,Wiki等许多格式的文档. Org特别适合于写科技类的文章,我主要用它来程序代码相关的东西.

org-mode还支持LaTex,下面是个例子:

- Unicode References :: for instance, \alpha, \beta and \gamma.

- Subscripts :: like Hydrogen atoms, H_2, and Water, H_{2}O.

- Superscripts :: The mass of the sun is 1.989 x 10^30 kg.

- Embedded Equations :: Surrounded with either single =$=, like $a^2=b$,

or escaped parenthesis, like: \( b=\frac{1}{2} \)

- Separated equations :: Either in double =$$= or escaped brackets, like

this: $$ a=\frac{1}{2}\sqrt{2} $$ or this: \[ a=-\sqrt{2} \] or this:

\begin{equation}

x=\sqrt{b}

\end{equation}

#+OPTIONS: tex:t

上面那段内容可以转换成下面的HTML展示:

Unicode References

for instance, α, β and γ.

Subscripts

like Hydrogen atoms, H[2], and Water, H[2]O.

Superscripts

The mass of the sun is 1.989 x 10^30 kg.

Embedded Equations

Surrounded with either single $, like \(a^2=b\), or escaped parenthesis, like: \( b=\frac{1}{2} \)

Separated equations

Either in double $$ or escaped brackets, like this: \[ a=\frac{1}{2}\sqrt{2} \] or this: \[ a=-\sqrt{2} \]

or this:

\begin{equation} x=\sqrt{b} \end{equation}

基础知识

这篇指南来源于一场研讨会, 那么让我们运行Emacs,创建一个org-mode文件,然后开始操作.

让我们输入以下代码块(大小写无关):

#+BEGIN_SRC emacs-lisp

(directory-files ".")

#+END_SRC

按下 C-c C-c 会执行该命令,并且将结果插入到文件后面 … we’ll use that to our advantage later.

按下 C-c '(单引号) 可以以指定语言的mode来编辑这段代码. 这样你就可以利用 paredit 或者类似的插件来帮你编辑代码了.

注意: 发现我的例子有错误后(注意,我并没有说”如果”), 去查一下 the org-mode manual,然后将勘误发给我.

快捷方式

若你使用比较新版本的Emacs(新于v22)或这时比较新版本的org-mode(你可能直接从ELPA中安装),那么你可以使用 Org Templates,它提供了如下功能:

输入 可以快速创建一个代码块

如果没有创建代码块,或许你可以试试 yasnippets

从代码块的 BEGIN 到 END 部分的任意位置,你都可以通过按下 C-c C-c 来执行代码

使用 C-c M-f 跳转到文件中下一个代码块位置,使用 C-c M-b 跳转到上一个代码块位置

推荐配置

要想语法高亮代码块中的代码,可以将下面代码放到 .emacs 初始化文件中:

(setq org-confirm-babel-evaluate nil

org-src-fontify-natively t

org-src-tab-acts-natively t)

将 org-confirm-babel-evalute 设置为nil,可以在你用 C-c C-c 执行代码块时,不再提示“Do you want to execute”

支持的语言

Org-mode支持许多的编程语言, 但还是有些语言是不支持的(当然,你可以为Org-mode添加新语言的支持,这并不难).

我会展示一些比较流行的语言案例,从中你也可以看出不同语言之间的些许差别.

Ruby案例

让我们将上面lisp的例子修改为Ruby:

#+BEGIN_SRC ruby

Dir.entries('.')

#+END_SRC

你再按下 C-c C-c 发现没有反映, 这是因为你需要预先加载好Ruby语言的支持: M-x load-library ob-ruby

你也可以将下面配置放到 .emacs 中:

(require 'ob-ruby)

Python案例

需要注意的是,Ruby和Lisp类似,会自动将最后表达式的值作为代码块的返回值. 然而,Python语言,需要明确的 return 语句:

#+BEGIN_SRC python

from os import listdir

return listdir(".")

#+END_SRC

shell案例

大多数的语言使用返回值作为结果,然而shell语言使用输出到标准输入的内容作为结果:

#+BEGIN_SRC sh

ls -1

#+END_SRC

若按下 C-c C-c 没反映,你需要执行: M-x load-library ob-sh

其他语言

若你像我一样,是个多语言开发者,你可以添加类似下面的配置到 .emacs 文件中:

(org-babel-do-load-languages

'org-babel-load-languages

'((sh . t)

(js . t)

(emacs-lisp . t)

(perl . t)

(scala . t)

(clojure . t)

(python . t)

(ruby . t)

(dot . t)

(css . t)

(plantuml . t)))

代码块设置

通过设置不同的代码块参数(也称为”头参数”)可以产生各种有趣的结果. 代码块可以有0个或多个头参数.

首先我们先讨论一下几种设置头参数的方式,然后在讨论这些参数的意义. 让我先以一个头参数为例.

例子: dir参数

下面以dir 参数为例来看如何设置一个参数, 该参数会设置代码块执行的工作目录:

#+BEGIN_SRC sh :dir /etc

ls

#+END_SRC

按下 C-c C-c 执行代码块,你会看到列出了许多 /etc 目录下的内容,当然前提是你有 /etc 目录…

该参数的一个有趣的用法是,可以通过Tramp实现在远程服务器上执行代码:

#+BEGIN_SRC sh

hostname -f

#+END_SRC

#+RESULTS:

: blobfish

#+BEGIN_SRC sh :dir /howardabrams.com:

hostname -f

#+END_SRC

#+RESULTS:

: goblin.howardabrams.com

设置头参数的位置

不同位置设置参数所影响的作用域也不一样. 下面几个位置都可以用于设置头参数,以作用域从特殊到一般的顺序列出.

头参数嵌入到代码块中,或者在代码块上面

设置某一标题下面所有代码块的默认头参数

设置整个文档中所有代码块的默认头参数

设置所有文档中所有代码块的默认头参数

对我来说,为所有文档设置某个参数没有什么意义. 但若你需要,可以设置下列参数:

org-babel-default-header-args

org-babel-default-header-args:

注意:你可以在代码块被调用时设置头参数. 我们还会在下面接着提到.

太多参数了?

以将参数嵌入代码块的方式设置少量头参数还行,但是org-mode本身支持许多的参数, 若你需要设置许多的参数的话, 可以考虑将一个或多个参数放到代码块的上面去. 比如下面几个例子都是等价的:

#+BEGIN_SRC sh :dir /etc :var USER="howard"

grep $USER passwd

#+END_SRC

#+HEADER: :dir /etc

#+BEGIN_SRC sh :var USER="howard"

grep $USER passwd

#+END_SRC

#+HEADER: :dir /etc

#+HEADER: :var USER="howard"

#+BEGIN_SRC sh

grep $USER passwd

#+END_SRC

设置某一标题下面所有代码块的默认头参数

若某一标题下面的代码块使用相同的头参数,则可以将头参数的设置放入标题下的属性drawer中. 你可以试试按照下面步骤操作:

在org文件中创建一个标题

输入 C-c C-x p

输入属性名称:dir

输入属性值: /etc

将光标定位到 :PROPERTIES: 处,然后按下 TAB 键就会显示出隐藏的内容. 整个内容看起来应该如下所示:

* A New Section

:PROPERTIES:

:dir: /etc

:END:

#+BEGIN_SRC ruby

File.absolute_path(".")

#+END_SRC

#+RESULTS:

: /etc

指定特定语言的默认头参数

你可以指定特定语言的头参数. 步骤如下:

在标题上按下 C-c C-x p

输入属性名称 header-args:sh

输入属性值 :dir /etc

输入 C-c C-x p

输入属性名称 header-args:ruby

输入属性值 :dir /

你会得到如下结果:

* Another Section

:PROPERTIES:

:header-args:sh: :dir /etc

:header-args:ruby: :dir /

:END:

#+BEGIN_SRC sh

ls -d $(pwd)

#+END_SRC

#+RESULTS:

: /etc

#+BEGIN_SRC ruby

File.absolute_path('.')

#+END_SRC

#+RESULTS:

: /

注意: 有些参数智能通过 header-args 的方式设置.

为文档内的所有代码块设置默认参数

通过将 #+PROPERTY: 设置项放到文档中,可以为该文档中的所有代码块设置默认参数

#+PROPERTY: dir ~/Work

注意: 这些参数并没有在前面带上冒号

要为特定语言的代码块设置,需要进行如下操作:

#+PROPERTY: header-args:sh :tangle no

注意: 你需要在设置项上按下 C-c C-c,否则该配置项不生效.

头参数的类型

基础的东西已经讲完了,剩下的内容就是讲讲各参数的意义了. 我按这些参数的用处将之分为以下几类:

执行类参数以 dir 为代表,这类参数影响代码块如何执行

导出类参数该类参数影响了当把org文件导出成HTML(或其他格式)时,代码块以及代码块的执行结果如何展示

文学编程类参数将代码块连接起来,可能会改变实际的源代码

变脸类参数通过不同方式设置代码块中的变脸

杂类 输入/输出其他参数

执行类参数

下列参数会影响到代码的执行情况.

results参数

当你执行代码块时,你希望得到哪个结果呢?

是表达式的返回值呢?

还是代码的输出结果?

以下面Ruby代码块为例. 默认情况下,你的到的是最后表达式的返回值:

#+BEGIN_SRC ruby

puts 'Hello World'

5 * 6

#+END_SRC

#+RESULTS:

: 30

若将:results 头参数的值修改为 output, 则的的结果是程序的输出:

#+BEGIN_SRC ruby :results output

puts 'Hello World'

5 * 6

#+END_SRC

#+RESULTS:

: Hello World

注意: sh代码块的 :results 默认值为 output.

影响输出格式的结果

代码的执行结果会插入到文档中,它可能以以下几种格式出入.

table若结果为单个数组,则插入一行,若结果为数组的数组,则插入一个表格

list按照普通org-mode列表的格式插入一个无序列表

verbatim原样输出

file将结果写入到文件中

html认为执行的结果是HTML代码,导出时原样导出

code认为执行的结果还是原语言的代码

silent只在mini-buffer中显示执行的结果

之所以有这么多的变种,是因为执行结果本身也可以被导出(可以以HTML,或Email等格式导出), 同时这些执行结果还能作为其他代码的输入变量. 这是文学编程中最有意思的部分了,我们后面还会再提到.

输出成列表

注意到之前的输出是以表格的格式插入的,这次我们以列表的格式来插入:

#+BEGIN_SRC ruby :results list

Dir.entries('.').sort.select do |file|

file[0] != '.'

end

#+END_SRC

#+RESULTS:

- for-the-host.el

- instructions.org

- literate-programming-tangling.png

- literate-programming-tangling2.png

上面是以Ruby为例,你可以试着使用你喜欢的语言来列出目录中的文件列表.

原样输出

Shell命令和日志输出比较适合使用原样输出,例如:

#+BEGIN_SRC sh :results verbatim :exports both

ssh -v goblin.howardabrams.com ls mossandcrow

#+END_SRC

#+RESULTS:

OpenSSH_6.6.1, OpenSSL 1.0.1f 6 Jan 2014

debug1: Reading configuration data /etc/ssh/ssh_config

debug1: /etc/ssh/ssh_config line 19: Applying options for *

debug1: Connecting to goblin.howardabrams.com [162.243.135.186] port 22.

debug1: Connection established.

debug1: identity file /home/howard/.ssh/id_rsa type 1

debug1: identity file /home/howard/.ssh/id_rsa-cert type -1

...

Session

默认情况下,每个代码块在每次运行时都会重启自己的一个解释器. 通过为:session 头参数设置一个标签值,则所有拥有同一标签的代码块在运行时都在同一个解释器session中. 为什么要这样做呢? 因为每次都重启解释器有以下几个问题:

有的解释器启动时间很长,例如Clojure

使用Tramp登录远程机器很慢

代码块共享函数定义与状态

注意: 由于不同代码块之间可以传递值,因此最后那个问题实际上可以绕过.

下面的例子说明了每个代码块执行时都会重启自己的解释器:

#+BEGIN_SRC python

avar = 42

return avar

#+END_SRC

#+RESULTS:

: 42

#+BEGIN_SRC python

return avar / 2

#+END_SRC

#+RESULTS:

NameError: global name 'avar' is not defined

而基于 :session 的解释器不会重启:

#+BEGIN_SRC ruby :session foobar

avar = 42

#+END_SRC

#+RESULTS:

: 42

#+BEGIN_SRC ruby :session foobar

avar / 2

#+END_SRC

#+RESULTS:

: 21

:session 常常设置为标题属性. 另外,你其实可以切换到 *foobar* 这个buffer中直接与解释器进行交互,你可以在那设置变量及其他状态,然后再执行代码块

下面的例子中有什么问题呢?

* Confusing Stuff

:PROPERTIES:

:session: stateful

:END:

#+BEGIN_SRC sh :results silent

NUM_USERS=$(grep 'bash' /etc/passwd | wc -l --)

#+END_SRC

We have access to them:

#+BEGIN_SRC sh

echo $NUM_USERS

#+END_SRC

#+RESULTS:

: 2

This doesn't return... why?

#+BEGIN_SRC ruby

21 * 2

#+END_SRC

警告: 为整个section设置的 :session 参数会影响到每个代码块,而不管该代码块是哪种编程语言. 这可能不是你想要的.

将结果写入到文件中

创建并执行下面这个代码块:

#+BEGIN_SRC ruby :results output :file primes.txt

require 'prime'

Prime.each(5000) do |prime|

p prime

end

#+END_SRC

你会发现执行的结果是插入了一个指向文件的链接. 点击该连接会在buffer中加载该文件.

注意: :file 参数需要与 :results output 共用,因为它不知道以哪种格式输出内部值

导出

按下 C-c C-e h o 会导出成HTML文件,并用浏览器打开.

:exports 头参数指明了哪些内容会被导出:

code只导出代码

results只导出结果

both同时导出代码与结果

none跳过该代码块,什么都不导出

注意: :exports 一般被设置成文件属性.

若导出成HTML时希望保持语法高亮,只需要加载htmlize 库即可:

(require 'htmlize)

该功能只在最近版本的org-mode中引入. 若没有新版本的org-mode,可以从ELPA上安装.

文学编程

编程时,有时需要从org-mode文件中抽取出源代码来创建源代码文件,这个过程成为tangling

Tangling

:tangle 参数会将所有同类语言的代码快中内容都写入指定的源码文件中.

#+BEGIN_SRC ruby :tangle double-space.rb

while s = gets

print s ; puts

end

#+END_SRC

拥有相同=:tangle= 值的代码块内容会按顺序写入到同一个文件中. 若 :tangle 参数值为 yes 则写入的文件其名称与原org文件名称一样(文件后缀不一样而已).

也可以使用 PROPERTY 来为整个文件的所有代码快指定一个值:

#+PROPERTY: tangle ~/.emacs.d/elisp/bling-mode.el

注释

若我需要与他人分享代码,我可以讲文档内容转换成注释:

Precede each line in the text from standard in (or file) with the

current line number.

See [[http://benoithamelin.tumblr.com/ruby1line][one liners]].

#+BEGIN_SRC ruby

while s = gets

puts "#{$<.file.lineno>

end

#+END_SRC

#+PROPERTY: tangle lineno.rb

#+PROPERTY: comments org

会tangle出下面这样的Ruby代码:

# Precede each line in the text from standard in (or file) with the

# current line number.

# See [[http://benoithamelin.tumblr.com/ruby1line][one liners]].

while s = gets

puts "#{$<.file.lineno>

end

:comments 参数指明了是否将以及如何将文档内容作为注释插入tangle出的代码. 其值为org表示将文档内容作为org code来格式化再作为注释插入.

注意: 只有代码快上面的内容才会作为注释插入.

若 :comments 的值为 link, 则插入的注释为链接到原org文件的连接.

由于基本上我只看文学编程的原文档内容(例如我的.emacs文件)–我几乎不会去看tangle出来的源代码,因此我觉得这个功能作用不大.

默认值为 no,意味着不插入任何注释.

Shebang

当创建脚本时,常常会指定脚本运行的解释器. 该解释器可以通过:shebang 参数来指定(可以以代码块的header或文档属性的方式来指定)

Precede each line in the text from standard in (or file) with the

current line number.

See [[http://benoithamelin.tumblr.com/ruby1line][one liners]].

#+BEGIN_SRC ruby :shebang "#!/usr/local/bin/ruby"

while s = gets

puts "#{$<.file.lineno>

end

#+END_SRC

#+PROPERTY: shebang #!/bin/ruby

#+PROPERTY: tangle lineno

#+PROPERTY: comments org

导出的结果为:

#!/usr/local/bin/ruby

# Precede each line in the text from standard in (or file) with the

# current line number.

# See [[http://benoithamelin.tumblr.com/ruby1line][one liners]].

while s = gets

puts "#{$<.file.lineno>

end

Noweb

若你为某个代码快命了名,则其他代码块就可以包含该代码快了.… 如标题所示,使用:noweb 参数.[fn:1] . 假设有如下一个Org文件:

Print the last field of each line.

#+NAME: the-script

#+BEGIN_SRC ruby

puts $F.last

#+END_SRC

#+BEGIN_SRC sh :noweb yes :tangle last-col.sh

ruby -ane '<>'

#+END_SRC

会创建一个名为 last-col.sh 的源代码文件,其内容为:

ruby -ane 'puts $F.last'

这个功能有什么用呢?

Donald Knuth当时所使用的老式语言,要求所有的变量和函数需要先定义后使用. 也就是说你需要从下到上的写代码.

然而有些代码适合以从上倒下的方式进行解释. 对于某些算法来说,比较适合使用web和tangle.

关于Noweb的警告

假设我们有一个包含多行的代码块,如下所示:

#+NAME: prime

#+BEGIN_SRC ruby

require "prime"

Prime.prime?(ARG[0])

#+END_SRC

#+BEGIN_SRC ruby :noweb yes :tangle primes.sh

cat $* | xargs ruby -ne '<>'

#+END_SRC

noweb引用前的文本会被当成最初的注释字符来看待(Treats the preceding text before the noweb reference like initial comment characters), 因此其产生的结果如下:

cat $* | xargs ruby -ne 'require "prime"

cat $* | xargs ruby -ne 'Prime.prime?(ARG[0])'

这需要shell中的here docs或单引号. 或者是Python中的三引号(This requires either here docs or single quotes in a shell, or triple quotes in Python):

cat $* | xargs ruby -ne '

'

变量

Org能够以变量的形式传递一个或多个值到你的代码块中. 下面演示一个静态地设置变量的例子:

#+BEGIN_SRC python :var interest=13

return 313 * (interest / 100.0)

#+END_SRC

#+RESULTS:

: 40.69

当然你可以再一行或多行位置上同时定义多个变量,下面是一个例子

#+HEADER: :var a=42 d=56 :var f=23

#+HEADERS: :var b=79 e=79

#+BEGIN_SRC ruby :var c=3 g=2

[ a, b, c, d, e, f, g ]

#+END_SRC

#+RESULTS:

| 42 | 79 | 3 | 56 | 79 | 23 | 2 |

但是像这样静态地设置变量的值有什么意义呢?

将代码块的结果作为值传递给另一个代码块

创建一个命名的代码块,如下所示:

#+NAME: twelve-primes

#+BEGIN_SRC ruby

require 'prime'

Prime.first 12

#+END_SRC

#+RESULTS: twelve-primes

| 2 | 3 | 5 | 7 | 11 | 13 | 17 | 19 | 23 | 29 | 31 | 37 |

注意到 RESULTS: 部分的名字与代码块的名字一样. 我们可以将该计算结果作为数组变量传递给另一个代码块:

#+BEGIN_SRC python :var primes=twelve-primes

return primes[-1]

#+END_SRC

#+RESULTS:

: 37

这也许是第一次Ruby与Python能够合作完成任务.

表格形式的变量数据

再下面的例子中,我需要一个填充了数字的表格. 我会通过一小段lisp程序来生成这个表格,但是你也可以用自己喜欢的语言来生成:

#+NAME: cool-numbers

#+BEGIN_SRC emacs-lisp

(mapcar (lambda (i)

(list i (random 10)

(expt i 2) (random 100)

(expt i 3) (random 1000)))

(number-sequence 1 10))

#+END_SRC

#+RESULTS: cool-numbers

| 1 | 1 | 1 | 14 | 1 | 74 |

| 2 | 7 | 4 | 25 | 8 | 823 |

| 3 | 2 | 9 | 68 | 27 | 402 |

| 4 | 4 | 16 | 17 | 64 | 229 |

| 5 | 6 | 25 | 4 | 125 | 208 |

| 6 | 7 | 36 | 67 | 216 | 203 |

| 7 | 0 | 49 | 96 | 343 | 445 |

| 8 | 0 | 64 | 58 | 512 | 908 |

| 9 | 2 | 81 | 15 | 729 | 465 |

| 10 | 0 | 100 | 61 | 1000 | 798 |

你无需拷贝这段源代码然后运行这段代码,你只需直接把这个充满数字的表格拷贝到你的文档就行了,像这样:

#+NAME: cool-numbers

| 1 | 1 | 1 | 14 | 1 | 74 |

| 2 | 7 | 4 | 25 | 8 | 823 |

| 3 | 2 | 9 | 68 | 27 | 402 |

| 4 | 4 | 16 | 17 | 64 | 229 |

| 5 | 6 | 25 | 4 | 125 | 208 |

| 6 | 7 | 36 | 67 | 216 | 203 |

| 7 | 0 | 49 | 96 | 343 | 445 |

| 8 | 0 | 64 | 58 | 512 | 908 |

| 9 | 2 | 81 | 15 | 729 | 465 |

| 10 | 0 | 100 | 61 | 1000 | 798 |

这并不会改变我们使用和处理这些数字的方式. 随便说一下,我经常创建数据表格,并使用表格中的数据作为测试函数时用的参数值,我会展示给你看我是如何操作的.

这个g名为 cool-numbers 的表格再代码块中会被替换成一个数组或数组的数组, 这里我们使用Python推导式来将该值分解为一个长长的数组. 然后对其中的每个数字加一

#+BEGIN_SRC python :var nums=cool-numbers :results list

return [ cell + 1 for row in nums for cell in row ]

#+END_SRC

#+RESULTS:

- 2

- 4

- 2

- 23

- 2

- 955

- 3

- 7

- 5

- 43

- 9

...

表格分片

我们可以只传递表格中的某一行,方法是指定一个索引编号. 你可以试试下面这短Ruby代码块:

#+BEGIN_SRC ruby :var fifth=cool-numbers[4]

fifth

#+END_SRC

#+RESULTS:

| 5 | 9 | 25 | 93 | 125 | 524 |

用类似的方法,我们也能只传递表格中的某一列数据. 下面是一个例子,其中的都号表示任意行,后面的4则限制了只取第5列的数字:

#+NAME: cubes

#+BEGIN_SRC elisp :var cubes=cool-numbers[,4]

cubes

#+END_SRC

#+RESULTS: cubes

| 1 | 8 | 27 | 64 | 125 | 216 | 343 | 512 | 729 | 1000 |

Reprocessing

名为cool-numbers的表格被名为cubes的代码块所使用,然后cubes代码快的结果值又可以传递给其他代码块:

#+NAME: roots_of_list

#+BEGIN_SRC python :var lst=cubes :results list

import math

return [ math.sqrt(n) for n in lst ]

#+END_SRC

#+RESULTS: roots_of_list

- 1.0

- 2.8284271247461903

- 5.196152422706632

- 8.0

- 11.180339887498949

- 14.696938456699069

- 18.520259177452136

- 22.627416997969522

- 27.0

- 31.622776601683793

保持代码块的整洁

代码块执行时可能与其他事物有关. 若一段代码需要执行,但这段代码并不需要告诉其他人,则这段代码可以放置再代码块的外部, 下面是一些例子.

设置环境

我经常使用nova命令查询OpenStack的实例. 该命令会从环境变量中读取数字证书, 这些环境变量一般设置在resource文件中. 一个典型的工作流程可能像下面这样:

$ source openrc

$ nova list

这里我想执行的代码是 nova list, 但是在执行该代码之前还需要执行source命令. 而该source命令我又不希望被导出. 则可以将这种不可见的代码放置在prologue 中

#+HEADER: :prologue "source openrc"

#+BEGIN_SRC sh

nova list

#+END_SRC

:prologue 中的代码不会背导出, 我的同事也只能看到 nova list 命令及其执行结果

Using RVM

类似Python和Ruby这类语言,经常会需要指定解释器. 你可以再 :prologue 的命令后加上两个反斜杠来表示代码块执行时的前缀(只对shell调用有效):

#+BEGIN_SRC sh :prologue "~/.rvm/bin/rvm 1.9.3@msw exec \\"

gem list

#+END_SRC

注意: Ruby或Python代码的执行时基于rvm, pyvenv 或 ELPY 的.

对结果进行修正

有时代码块的执行结果并不就是我们想导出到文档中的样子. While we could probably change the code, perhaps our point is the code as written.

例如, shell命令 ls -l 的结果会在最开始的地方添加一个指明有多少总数的行:

下例中的 ls 命令带了一个 time-style 参数:

#+BEGIN_SRC sh

ls -lhG --time-style long-iso

#+END_SRC

#+RESULTS:

| total | 5.8M | | | | | |

| -rw-rw-r-- | 1 | howard | 6.0K | 2015-09-02 | 17:36 | emacs-init.org |

| -rw-rw-r-- | 1 | howard | 22K | 2015-07-05 | 11:13 | eshell-fun.org |

| -rw-rw-r-- | 1 | howard | 3.0K | 2015-07-05 | 11:13 | eshell.org |

| -rw-rw-r-- | 1 | howard | 4.3K | 2015-09-02 | 12:52 | getting-started2.org |

| -rw-rw-r-- | 1 | howard | 5.1K | 2015-03-30 | 18:08 | getting-started.org |

...

第一行搞乱了我们的表格. 我们可以使用 tail 命令来修正我们的代码:

#+BEGIN_SRC sh

ls -lhG --time-style long-iso | tail -n +2

#+END_SRC

然而,在该例中,我想讲的时 ls 命令而不是 tail 命令. =tail=命令在这很突兀.

我们可以使用:post 参数来修正代码块的执行结果, 这样我们可以不修改代码块而获得想要的结果.

在本例中,要删除掉第一行,我们要创建一个代码块处理器来返回除了第一行之外的所有行. 我设定该代码块处理器的 :exports 参数为 none 因为我不希望它被导出.

请注意data变量:

#+NAME: skip_first

#+BEGIN_SRC elisp :var data="" :exports none

(cdr data)

#+END_SRC

现在我们的代码块中可以只包含 ls -l 命令了,但是我们还需要讲结果传递给 skip_first 代码块进行处理.

我们为data变量赋值为 *this* (表示当前代码块的输出结果). 现在我们的结果中只包含文件了:

#+BEGIN_SRC sh :post skip_first(data=*this*)

ls -lhG --time-style long-iso

#+END_SRC

#+RESULTS:

| -rw-rw-r-- | 1 | howard | 6.0K | 2015-09-02 | 17:36 | emacs-init.org |

| -rw-rw-r-- | 1 | howard | 22K | 2015-07-05 | 11:13 | eshell-fun.org |

| -rw-rw-r-- | 1 | howard | 3.0K | 2015-07-05 | 11:13 | eshell.org |

| -rw-rw-r-- | 1 | howard | 4.3K | 2015-09-02 | 12:52 | getting-started2.org |

| -rw-rw-r-- | 1 | howard | 5.1K | 2015-03-30 | 18:08 | getting-started.org |

...

当我们在后面讨论Tower of Babel时,就会发现 :post 参数真的很有用, 因为我们可以创建一系列的输出处理器,在其他文档中使用.

其他特性

以下是一些不好分类的参数和特性.

调用代码块

目前为止,我们是通过 :var 参数来为代码块设置值的, 我们还可以再调用代码块是给代码块赋值.

还记得我们之前创建过的那个名为roots_of_list的代码块吗? 该代码块接受一个名为lst的变量. 下面来演示一下如果使用一个不同的值来为该变量赋值:

#+CALL: roots_of_list( lst='(16 144 81 61) )

#+Results:

| 4.0 | 12.0 | 9.0 | 7.810249675906654 |

我们还可以使用其他代码块的输出结果. 下面例子中就使用的时 cool-numbers 表格中的一列作为被传递的值.

#+CALL: roots_of_list( lst=cool-numbers[,2] )

#+RESULTS:

| 1.0 | 2.0 | 3.0 | 4.0 | 5.0 | 6.0 | 7.0 | 8.0 | 9.0 | 10.0 |

注意: 你可以在中括号内为代码块设置其他头参数的值. 详情请参见 Evaluating code blocks.

那么调用代码块在导出时是怎样的呢? 这个得看了. 若代码块运算结果像下例一样返回单个值:

#+NAME: cube

#+BEGIN_SRC elisp :var n=0 :exports none

(* n n n)

#+END_SRC

那么,在设定结果为table格式的情况下调用它,其结果与平常没什么不同:

#+CALL: cube[:results table](n=3)

但过在设定结果为list格式的情况下调用它,则导出的结果是嵌入

 块中.

嵌入运算结果

如果你想得快速得到一门语言片段的计算结果,你可以在大括号内嵌入这段代码. 例如,可以试试将下面内容输入你的org文件中,然后在行首按下 C-c C-c 看看结果:

src_ruby{ 5+6 } =11=

其结果,11,会添加在代码块后面. 当导出时,只有计算结果会被导出(源代码不会被导出).

我想这项功能应该常用于生成文档内容,像下面这样:

We will be bringing src_ruby{ 5+6 } children.

注意: 计算的结果会被嵌入到HTML的 标签中.

你也可以插入shell脚本的执行结果:

Why do I have src_sh{ ls /tmp | wc -l } files?

甚至可以插入Emacs Lisp函数的返回值:

src_elisp{ org-agenda-files }

也支持插入调用代码块的值. 例如,假设我们定义了一个名为roots_of_list的代码块,则可以这样:

call_roots_of_list( lst=cool-numbers[,2] )

| 1.0 | 2.0 | 3.0 | 4.0 | 5.0 | 6.0 | 7.0 | 8.0 | 9.0 | 10.0 |

警告: 当我再演示这项功能时,发现若你在 src_XYZ 上按下了 C-c C-c 会插入代码块的计算结果. 然后再导出时会内嵌两次结果. 一次是执行代码块时生成的结果,一次是内嵌的那个结果(这个问题现在已经被修复).

Library of Babel

Library of Babel 是一系列可以在任意org-mode文件总调用的代码块. 就好像时Ruby中的Gem源一样, 你需要指定哪些包含有命名代码块的文件是可访问的.

按照以下步骤进行操作:

新建一个org文件,并添加至少一个命名了的代码块

按下 C-c C-v i

选择你新建的这个org文件,表示将该文件加入babel集合中.

用一个你常用的代码块时试一试:

#+NAME: take

#+BEGIN_SRC elisp :var data='() only=5

(require 'cl)

(flet ((take (remaining lst)

(if (> remaining 0)

(cons (car lst) (take (1- remaining) (cdr lst)))

'("..."))))

(take only data))

#+END_SRC

你保持该新建的org文件,并将其作为babel addition加载后, 就可以将该代码片段用于 :post 参数了:

#+BEGIN_SRC python :post take(data=*this*, only=3)

return [x * x for x in range(1, 20)]

#+END_SRC

#+RESULTS:

| 1 | 4 | 9 | ... |

该功能在以下几种情况下很有用:

用于 :post 参数中处理结果

通过 #+CALL 语句将运算结果嵌入到行中

通过 call_XYZ() 语句将运算结果嵌入到行中

要想让这些文件永久性的添加到 babel library中,需要在你的Emacs初始化文件中对每个想被添加的org文件调用 org-babel-lob-ingest 函数.

专用语言

我发现有些语言被org-mode所支持是为了更好的编写文档的(I’ve found a few programming languages that really add to an org-mode way of writing documents).

SQL

通过SQL语句查询数据库,然后使用其他语言处理查询结果,这种能力十分有用.

and if I felt I could have used them, would have made this workshop-tutorial less trivial (but also less accessible).

假设你已经安装了Sqlite, 并且通过 M-x load-library ob-sqlite 加载了必要的库:

你可以在Sqllite命令行中使用 .backup 命令导出一个数据库,然后在 :db 参数中指定该数据库. 就像下面这样:

#+BEGIN_SRC sqlite :db dolphins.db

SELECT gender,COUNT(gender) FROM oasis GROUP BY gender;

#+END_SRC

#+RESULTS:

| f | 55 |

| m | 89 |

其结果时一个简单的表格:

f55

m89

Graphviz

若你安装了Graphviz , 则我们可以直接再文档中创建图标:

#+BEGIN_SRC dot :file some-illustration.png

digraph {

a -> b;

b -> c:

c -> a;

}

#+END_SRC

37ac303e9e2c5c7496472ec735a8a359.png

对这种应用,我一般会设置 :exports results 以便在导出时不要导出产生图片的代码.

警告: 若你希望执行代码块并生成图片,则需要设置代码块的语言类型为 dot, 但若你想编辑该代码块,则又需要把语言类型设置为 graphviz-dot.

PlantUML

若你安装了PlantUML ,你可以实现类似下面的功能:

#+BEGIN_SRC plantuml :file sequence.png :exports results

@startuml sequence-diagram.png

Alice -> Bob: synchronous call

Alice ->> Bob: asynchronous call

@enduml

#+END_SRC

b0c28317073318f48d2b17f8eb9208ff.png

哈哈,你可以为你的源代码插入描述性插图了.

Calc

我们已经接触过了令人印象深刻的 Emacs Calculator 及其常用的数学符号.

#+BEGIN_SRC calc :var a=2 b=9 c=64 x=5

((a+b)^3 + sqrt(c)) / (2x+1)

#+END_SRC

#+RESULTS:

: 121.727272727

若我们没有对某些变量赋值的话,则会简化这个方程式:

#+BEGIN_SRC calc :var a=4 b=2

((a+b)^3 + sqrt(c)) / (2x+1)

#+END_SRC

#+RESULTS:

: (sqrt(c) + 216) / (2 x + 1)

当然,你需要通过 M-x load-library ob-calc 加载必要的库.

注意每个calc代码块的每一行都会进入Calc mode buffer的栈中(使用 C-x * * 可以切换到Calc中查看).

总结

下面是针对头参数 的说明清单,按你的目标或需求分类:

代码执行?

dir :: 指定代码执行的工作目录,支持Tramp

session :: 在不同代码块之间共享解释器

file :: 将代码块的计算结果写入文件中

eval :: 限制只指定特定的代码块

cache :: 缓存计算结果,防止对代码块重复计算

var :: 为代码块赋值(ignore with no-expand)

导出?

results :: 结果时要输出的内容还是返回值,格式时怎样的

exports :: 代码与结果该如何导出

文学编程?

tangle :: 源代码以何种方式写入到脚本文件中 … 这时文学编程的核心.

mkdirp :: 再tangle源码文件时是否创建父目录

shebang :: 写入到源码文件中第一行的内容

noweb :: 是否扩展noweb引用

noweb-ref :: noweb引用的内容

Special Input?

prologue :: 参数决定了在执行代码块中的代码之前,作什么初始化操作

epilogue :: 参数决定了在执行代码块中的代码之后,作什么清理操作

Special Output and Formatting?

padline

post :: 参数用于决定了得到代码块的result后,该result要传递到哪个代码块中作进一步的处理

wrap

其他.

hlines, colnames, rownames

Footnotes

[fn:1] 参见链接

The term noweb is from Knuth’s original program for tangling out source code. Since each code block could be

re-inserted into other blocks, he saw this as creating a tangled web of connections.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
您可以使用 Emacs 中的 Verilog 模式来自动实例化您的模块。以下是您可以遵循的步骤: 1. 打开您的 .v 文件并进入 Verilog 模式。您可以使用以下命令: ``` emacs filename.v M-x verilog-mode ``` 2. 在您的 .v 文件的顶部,使用 `module` 关键字定义您的模块。例如: ``` module my_module(input clk, input [7:0] data_in, output reg [7:0] data_out); ``` 3. 在您的 .v 文件中,您可以使用 `inst` 关键字将模块实例化。例如: ``` my_module_inst : my_module port map ( clk => clk, data_in => data_in, data_out => data_out ); ``` 这将在您的 .v 文件中创建一个模块实例,其中 `my_module_inst` 是您要创建的实例的名称,`my_module` 是您模块的名称,`port map` 是用于将端口连接到实例的关键字。 4. 在您的 .v 文件中,您可以使用 `generate` 关键字创建一个自动生成模块实例的块。例如: ``` generate my_module_inst : my_module port map ( clk => clk, data_in => data_in, data_out => data_out ); endgenerate ``` 这将在您的 .v 文件中创建一个自动生成模块实例的块,其中 `my_module_inst`、`my_module` 和 `port map` 与前面的示例相同。 5. 最后,在您的 .v 文件中,您可以使用 `include` 命令将模块自动实例化到另一个文件中。例如: ``` `include "tb_template.v" ``` 其中 `tb_template.v` 是您要将模块实例化到的文件名。 6. 保存您的 .v 文件并退出 Emacs。模块的实例将自动添加到您指定的文件中。 希望这些步骤对您有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值