我想要一个elisp脚本在光标下执行Python一行代码

2 投票
2 回答
1289 浏览
提问于 2025-04-17 15:19

我刚开始学Emacs,这个月初才开始用。

我想把一些小的Vim脚本移植到Emacs上,这样我们也能在Emacs里进行类似的计算。

http://www.youtube.com/watch?v=yDR0dTPu6M4

我尝试移植下面这个Vim脚本。

function! s:ExecPySf_1liner()
    let l:strAt = getline(".")
    call writefile([strAt], "temp.pysf")

    let l:strAt = system("python -u -m sfPP -fl temp.pysf")
    if @0 == 0
        let @0 = l:strAt
    else
        let @0 = l:strAt
    endif

    let @" = @0
    if match(&clipboard, "unnamed") >= 0
        let @* = @0
    endif
    echo @0
endfunction         

但是我真的累坏了,花了整整三天才写出下面的代码。

(defun ExecPySf_1liner ()
    (let (  (strAt
             (buffer-substring-no-properties (point-at-bol) (point-at-eol))
            )
         )
    ) 
)

我想让Emacs执行以下操作。

1 read one line under the cursor.
2 write down the one line string into temp.pysf file in current directory
3 execute "python -u -m sfPP -fl temp.pysf" in a shell.
4 display the returned calculated string in echo arear
5 and copy the string in the clipboard to enable a user to past the calculated result.

请告诉我对应的elisp函数或代码。

提前谢谢你!

===============================

嗨,Chris。我把你的代码修改成了下面这样。

(defun __getLineOmittingComment ()
    "Get position after ';;' string. If there is no ;; then return line-beginning-posiion"
    (interactive)
    (goto-char (line-beginning-position))
    (let (( posAt (search-forward ";;" (line-end-position) t) ))
     (if (equal posAt nil) (line-beginning-position) posAt)
    )
)

(defun ExecPySf_1liner()
    "Evaluates the current line in PythonSf, then copies the result to the clipboard."
    (interactive)
    (write-region (__getLineOmittingComment) (line-end-position) "temp.pysf" nil)

    (let ((strAt
           (shell-command-to-string "python -u -m sfPP -fl temp.pysf" )
         ))
        (message strAt)
        (kill-new strAt)))

ExecPySf_1liner() 用来计算勒让德符号:http://en.wikipedia.org/wiki/Legendre_symbol,如下所示。

import sympy as ts; Lgndr=lambda a,b:(lambda c=a%b:0 if ts.gcd(c,b)!=1 else 1 if any((c-k^2)%b==0 for k in range(1,b//2+2)) else -1)(); [Lgndr(3,p) for p in ts.primerange(3,100)] 
===============================
[0, -1, -1, 1, 1, -1, -1, 1, -1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1, -1, 1, -1, 1]

你应该看看IPython,它有一个Emacs模式,可以完成所有这些功能,甚至更多。

我理解你的观点,但你可能忽略了Python的一行代码是函数式编程,并且是以一行完成的。因为它们不使用if-then-else语法,而是必须使用lambda函数,不使用def函数。虽然它们不完全是引用透明的,但它们和elisp脚本一样,都是函数式编程。而且数学问题用函数式编程风格写起来很简单,比如勒让德符号。

IPython可以把笔记保存成Matlab、Mathematica格式,你可以重复使用它们。但笔记的内容往往会很复杂。普通人在写出有价值的表达式之前,通常会写出很多不好的表达式。而有价值的表达式往往依赖于一些之前的表达式,这样就很麻烦去整理这些依赖的表达式。所以笔记就会变得很混乱。

一年后,当你想再次使用有价值的表达式时,你可能会忘记笔记的细节,这样就很难再使用这些有价值的表达式,因为你必须记住整体的细节。

但每个Python的一行代码都是独立完整的,即使过了几年你也能轻松重复使用。因为它们是函数式编程,所以你可以很容易地合并Python的一行代码。

你可能会发现处理Python的一行代码比在IPython中处理Python表达式更简单。


我从修改你的elisp代码中学到了很多东西。我开始喜欢elisp了,可能会比Vim脚本更熟悉elisp。非常感谢你。

===============================================================================

你应该看看IPython :) 它有一个Emacs模式,可以完成所有这些功能,甚至更多。 :) ? 我理解你的观点,但我认为用Emacs作为IPython比用IPython作为Emacs更好。

我稍微扩展了一下Python以适应数学需求。sfPP.py是一个预处理器,它会将一行代码转换成下面的代码。你不需要写“print”,因为sfPP.py会自动添加打印指令。

' xy"' in 'abcd"xy"efg'
===============================
False

type __tempConverted.py
from __future__ import division
# -*- encoding: utf-8 -*-
from pysf.sfFnctns import *
setDctGlobals(globals())
from pysf.customize import *
if os.path.exists('./sfCrrntIni.py'):
    from sfCrrntIni import *
rightSideValueAt__= ' xy"' in 'abcd"xy"efg'
print "==============================="
print rightSideValueAt__
putPv(rightSideValueAt__, '_dt')

'"xy"' in 'abcd"xy"efg'
===============================
True

(这个示例代码也说明了我为什么敢用临时的一行代码文件。聪明的Chris会理解这个原因。)

你可以很容易地在Emacs中查看Python源代码,如下所示。

source(np.source)
In file: C:\Python27\lib\site-packages\numpy\lib\utils.py

def source(object, output=sys.stdout):
        snipped
    import inspect
    try:
        print >> output,  "In file: %s\n" % inspect.getsourcefile(object)
        print >> output,  inspect.getsource(object)
    except:
        print >> output,  "Not available for this object."

===============================
None

你应该看看IPython。我一直在看IPython的YouTube视频:“深入了解IPython”,想写一篇文章:“用Vim/Emacs作为IPython”。

你同意我用Emacs作为IPython吗?


最后一个问题。我想点击接受按钮,但我不知道它在哪里。“这个帖子对你有用吗?是/否按钮”可能不是那个。

2 个回答

0

如果有其他人看到这个,我对@Chris Barrett做了一些修改,以防止Python的print在输出时自动加上换行符。另外,不知道为什么用-m module来导入这个模块时,命令没有任何输出,所以我选择在命令中直接导入这个模块。

(defun run-python-command (str)
  (shell-command-to-string
   (concat "/usr/bin/python -c "
           (shell-quote-argument (concat "from myutils import *; import sys; sys.stdout.write(" str ")")))))
3

这是我快速写的一个东西:

(defun get-current-line ()
  (buffer-substring-no-properties (line-beginning-position)
                                  (line-end-position)))

(defun run-python-command (str)
  (shell-command-to-string
   (concat "/usr/bin/env python -u -m sfPP -c "
           (shell-quote-argument (concat "print(" str ")")))))

(defun eval-line-in-python ()
  "Evaluates the current line in python, then copies the result to the clipboard."
  (interactive)
  (let ((str (run-python-command (get-current-line))))
    (message str)
    (kill-new str)))

你可以用 M-x eval-line-in-python 来运行它。

我把它改成了不使用临时文件,而是直接执行这一行代码。如果你还是想写一个临时文件,改起来也很简单。

撰写回答