# 给孩子做个加减法算术习题本--如何在latex中使用python及pythontex功能简介
# 给孩子做个加减法算术习题本--如何在latex中使用python及pythontex功能简介 # 给孩子做个加减法算术习题本--如何在latex中使用python及pythontex功能简介 # 给孩子做个加减法算术习题本--如何在latex中使用python及pythontex功能简介 # 给孩子做个加减法算术习题本--如何在latex中使用python及pythontex功能简介 # 给孩子做个加减法算术习题本--如何在latex中使用python及pythontex功能简介
# 给孩子做个加减法算术习题本--如何在latex中使用python及pythontex功能简介
# 给孩子做个加减法算术习题本--如何在latex中使用python及pythontex功能简介
# 给孩子做个加减法算术习题本--如何在latex中使用python及pythontex功能简介
# 给孩子做个加减法算术习题本--如何在latex中使用python及pythontex功能简介
# 给孩子做个加减法算术习题本--如何在latex中使用python及pythontex功能简介
作品简介

latex 是用于排版的,严格上不算真正的编程语言,因此若需要一些复杂一点的计算功能,最好还是借助其它语言。比如:随机数在latex也是有的,加减法也是可以做的,但想实现一个20或100以内加减法随机算式的小册子,我还是首先想到用python来获得随机的计算式。所以本文讲讲怎么利用latex结合python来实现我们的目标,获得一个由随机计算式构成的小册子。

latex 中使用python有多种方法,比如:利用python宏包、pythontex宏包等。目前发展的功能比较完整的是pythontex宏包。由于latex宏包众多,即便我这种业余使用latex多年的人,也只是熟悉部分的宏包,所以要使用python,我们先来学习怎么使用pythontex,其实这也是所有latexer的常态,5000多个宏包不可能记的过来,有些文档还特别长,更别说其中大量的自定义的命令或宏。

pythontex的学习

pythontex的基础知识

搜索pythontex的相关文档主要有三个,pythontex.pdf,pythontex_gallery.pdf,pythontex_quickstart.pdf。通常这种带quickstart、tutorial,guide这些字眼的文档是快速入门的重要文档。我们先来看看它的内容。

第一部分讲安装我们不用管了,因为texlive完全安装,通常都已经OK了。第二部分讲编译,只有一个关键点,就是中间步需要使用pythontex.py进行编译。至于tex引擎则使用pdftex、xetex、luatex都行。所以对于习惯使用xelatex的我来说,编译步骤应该是(注意下面列出的步骤没有考虑其它内容需要其它工具的编译问题,比如bibtex/biber等):

xelatex test.tex
pythontex.py test.tex
xelatex test.tex 

第三和四部分讲基本命令和环境。其中pycode, pyblock, pyverbatim, pysub 环境分别等价于命令\pyc\pyb\pyv\pys。具体如下:

  • \py 返回其参数执行代码得到的结果的字符串表示,特别注意其中的python代码是存在返回的,若没有指定返回则返回None。

  • \pyc 执行参数中的代码并保存结果,且保存变量定义便于后面在\py 命令中调用,比如:\pyc{var = 2} 创建了一个变量,可以在其后用\py{var} 来调用。

  • \pyb 执行参数中的代码并输出这些代码。比如:\pyb{var = 2} 除了创建了一个变量也输出 var = 2

  • \pyv 仅将代码输出,而不执行。比如:\pyv{var = 2} 输出文本 var = 2.

  • \pys 执行代码中的变量替换. 需要替换的区域用!{...}表示。比如:使用之前定义的var变量,那么命令 \pys{\verb|var = !{var}|} 将得到 var = 2.

需要注意的是还有一些命令和环境以sympy和pylab开头,这些命令和环境默认会将sympy和pylab模块引入,使得包含在其中的python代码不用再加载这些python模块,这在第五部分介绍。

另外pyconsole环境可以产生类似终端的效果,不仅包命令输出,也输出计算结果。比如:

\begin{pyconsole}
var = 1 + 1
var
\end{pyconsole}

会得到:

>>> var = 1 + 1
>>> var
2

而其中的变量可以利用\pycon命令访问。

第6部分讲了一下使用python2情况下的问题,通常我们使用python3,所以无需关注。pythontex也支持其它语言,因为机制本身是相通的。这在第7部分介绍了一下。第8部分讲了一下可以在latex宏中使用前述命令,但要避免在python代码中使用latex特殊字符,比如%等,这很好理解,如果在宏中使用%那么tex引擎可能会认为其后的内容是注释内容。第9部分讲了一些功能和相关工具,如depythontex等。第10部分讲了pythontex输出代码是基于fancyvrb和fvextra宏包的,所以两者的设置可以正常使用,也可以将相关可选设置作为pythontex提供环境的参数传递进来。第11部分介绍了unicode支持,明确pythontex在任意tex引擎下的使用方式。

pythontex的示例测试

pythontex提供的三个文档中,pythontex_gallery.pdf提供了一些示例,通过这些示例的实践,我们可以快速的掌握pythontex的使用。实践代码如下:

\documentclass{article}
\usepackage{geometry}
\usepackage{ctex}
\usepackage{pythontex}%[pyginline=true]
\usepackage{amsmath}
\usepackage{graphicx}

\begin{document}

\section{简单命令和环境}

需要注意:python中print的输出等价于于tex文档内部的文本输入

\py{2 + 4**2} %存在返回

\pyc{print('Python says hi!')}

\pyc{print(str(2**2**2) + r'\par')}

\pyc{print('$1 + 1 = {0}$'.format(1 + 1))}


\section{python代码的高亮显示}

在不同的环境下python代码都是高亮的,比如:

\begin{pyconsole}
x = 123
y = 345
z = x + y
z

def f(expr):
    return(expr**4)

f(x)

print('Python says hi from the console!')
\end{pyconsole}

pygment命令也可以高亮显示行内的python代码,比如:\pygment{python}|sum=0|,其中第一个参数是代码的语言。

\section{复杂的python运算:符号运算}

利用sympy开头的环境进行符号运算,如:

\begin{sympyblock}
var('x, y, z')
z = x + y
\end{sympyblock}

\begin{sympycode}
from scipy.integrate import quad
from scipy.constants import *

var('x, y, z')
z = x + y
print(r'\begin{align*}')
print(latex(z)+r"\\")

f = x**3 + cos(x)**5
g = Integral(f, x)
print(latex(g)+r"\\")

phi = Symbol(r'\phi')
h = Integral(exp(-phi**2), (phi, 0, oo))
print(latex(h)+r"\\")


myintegral = quad(lambda x: e**-(x**2), 0,1)[0]
print(latex(myintegral)+r"\\")

print(r'\end{align*}')
\end{sympycode}


\begin{sympycode}
x, y, z = symbols('x,y,z')
f = Symbol('f(x,y,z)')

# Define limits of integration
x_llim = 0
x_ulim = 2
y_llim = 0
y_ulim = 3
z_llim = 0
z_ulim = 4

print(r'\begin{align*}')

# Notice how I define f as a symbol, then later as an actual function
left = Integral(f, (x, x_llim, x_ulim), (y, y_llim, y_ulim), (z, z_llim, z_ulim))
f = x*y + y*sin(z) + cos(x+y)
right = Integral(f, (x, x_llim, x_ulim), (y, y_llim, y_ulim), (z, z_llim, z_ulim))

print(latex(left) + '&=' + latex(right) + r'\\')

# For each step, I move limits from an outer integral to an inner, evaluated
# integral until the outer integral is no longer needed
right = Integral(Integral(f, (z, z_llim, z_ulim)).doit(), (x, x_llim, x_ulim),
 (y, y_llim, y_ulim))

print('&=' + latex(right) + r'\\')

right = Integral(Integral(f, (z, z_llim, z_ulim), (y, y_llim, y_ulim)).doit(),
(x, x_llim, x_ulim))
print('&=' + latex(right) + r'\\')

right = Integral(f, (z, z_llim, z_ulim), (y, y_llim, y_ulim),
(x, x_llim, x_ulim)).doit()
print('&=' + latex(right) + r'\\')

print('&=' + latex(N(right)) + r'\\')

print(r'\end{align*}')
\end{sympycode}

注意运算中的doit函数。

使用pycode环境并在代码中加入from sympy import * 等价于使用sympycode环境。利用sympy进行公式的推导也是不错的。

\begin{pycode}
from sympy import *
from re import sub

var('x')

# Create a list of functions to include in the table
funcs = ['sin(x)', 'cos(x)', 'tan(x)',
'sin(x)**2', 'cos(x)**2', 'tan(x)**2',
'asin(x)', 'acos(x)', 'atan(x)',
'sinh(x)', 'cosh(x)', 'tanh(x)']

print(r'\begin{align*}')

for func in funcs:
    # Put in some vertical space when switching to arc and hyperbolic funcs
    if func == 'asin(x)' or func == 'sinh(x)':
        print(r'&\\')

    myderiv = 'Derivative(' + func + ', x)'
    myint = 'Integral(' + func + ', x)'
    print(latex(eval(myderiv)) + '&=' +
    latex(eval(myderiv + '.doit()')) + r'\quad & \quad')
    print(latex(eval(myint)) + '&=' +
    latex(eval(myint+'.doit()')) + r'\\')

print(r'\end{align*}')
\end{pycode}

\section{复杂的python运算:绘图}

利用pylab开头的环境进行绘图

\begin{pylabcode}
rc('text', usetex=True)
rc('font', family='serif')
rc('font', size=10.0)
rc('legend', fontsize=10.0)
rc('font', weight='normal')
x = linspace(0, 10)
figure(figsize=(4, 2.5))
plot(x, sin(x), label='$\sin(x)$')
xlabel(r'$x\mathrm{-axis}$')
ylabel(r'$y\mathrm{-axis}$')
legend(loc='lower right')
savefig('myplot.pdf', bbox_inches='tight')
\end{pylabcode}

\begin{figure}[!h]
\centering
\includegraphics[width=0.5\linewidth]{myplot.pdf}
\caption{利用python绘图后插入图片}
\end{figure}

\end{document} 

结果为:

简单命令

符号运算1

符号运算2

绘图

通过这个简单的示例,我们已经明白了pythontex的基本使用方式。

编译方式为(注意:为了避免路径问题导致编译错误,可以使用绝对路径):

xelatex eg.tex
python C:\texlive\2020\texmf-dist\scripts\pythontex\pythontex.py D:\work-latex\test\b\eg.tex
xelatex eg.tex

算术练习册实现

掌握了pythontex,那么实现一个练习册就是一个组装的工作了。

我们用一个A5的纸张,所以设置一下页面:

\usepackage[a5paper,left=0.5cm,right=0.5cm,top=1cm,bottom=1.5cm]{geometry}

分成三栏,所以用一下multicol宏包,同时利用ctex设置一下段首的缩进:

\usepackage[autoindent=1em]{ctex}
\usepackage{multicol}
\setlength{\columnsep}{0.1em}
\setlength\columnseprule{0.4pt}

填写的位置用田字格表示,那么利用tikz画个田字格:

\usepackage{tikz}
\usetikzlibrary{shapes,snakes}
\tikzset{
    milines/.style={very thin,dash pattern=on 0.1em off 0.1em, black!50},
    mibox/.style={thin},
}
\def\hanzimibox{\raisebox{-0.35em}{\tikz{%米字格形状
    \def\xst{0}
    \def\yst{0}
    \def\xed{1.3em}
    \def\yed{1.35em}
    \draw[mibox](\xst,\yst) rectangle (\xed,\yed);
    %\draw[milines](\xst,\yst) -- (\xed,\yed);
    %\draw[milines](\xst,\yed) -- (\xed,\yst);
    \draw[milines](\xst,\yed/2) -- (\xed,\yed/2);
    \draw[milines](\xed/2,\yst) -- (\xed/2,\yed);
    }}}

最后用一个pycode环境,利用python代码使用随机数,循环,输出需要的算式(20和100以内的加减)。最终的形式如下:

\documentclass{article}
\usepackage[a5paper,left=0.5cm,right=0.5cm,top=1cm,bottom=1.5cm]{geometry}
\usepackage[autoindent=1em]{ctex}
\usepackage{pythontex}
\usepackage{amsmath}

\usepackage{multicol}
\setlength{\columnsep}{0.1em}
\setlength\columnseprule{0.4pt}

\usepackage{tikz}
\usetikzlibrary{shapes,snakes}
\tikzset{
    milines/.style={very thin,dash pattern=on 0.1em off 0.1em, black!50},
    mibox/.style={thin},
}
\def\hanzimibox{\raisebox{-0.35em}{\tikz{%米字格形状
    \def\xst{0}
    \def\yst{0}
    \def\xed{1.3em}
    \def\yed{1.35em}
    \draw[mibox](\xst,\yst) rectangle (\xed,\yed);
    %\draw[milines](\xst,\yst) -- (\xed,\yed);
    %\draw[milines](\xst,\yed) -- (\xed,\yst);
    \draw[milines](\xst,\yed/2) -- (\xed,\yed/2);
    \draw[milines](\xed/2,\yst) -- (\xed/2,\yed);
    }}}




\begin{document}

\zihao{-2}


\begin{multicols}{3}
\begin{pycode}
import random

neq=0
while(True):
    #a=random.randint(0, 20)
    #b=random.randint(0, 20)
    c=random.randint(0, 1)
    #print(random.choices(range(21),k=2))
    lst=random.choices(range(21),k=2)
    a,b=lst[0],lst[1]
    if(c==0):
        if a+b<=20:
            print("%2s"%a," + ","%2s"%b," ="+r"\mbox{}\hanzimibox"+"\n")
            neq+=1
    else:
        if(a>=b):
            print("%2s"%a," - ","%2s"%b," ="+r"\mbox{}\hanzimibox"+"\n")
            neq+=1
    if neq>1000:
        break

\end{pycode}

\begin{pycode}
import random

neq=0
while(True):
    #a=random.randint(0, 100)
    #b=random.randint(0, 100)
    c=random.randint(0, 1)
    #print(random.choices(range(101),k=2))
    lst=random.choices(range(101),k=2)
    a,b=lst[0],lst[1]
    if(c==0):
        if a+b<=100:
            print("%2s"%a," + ","%2s"%b,"="+r"\mbox{}\hanzimibox"+"\n")
            neq+=1
    else:
        if(a>=b):
            print("%2s"%a," - ","%2s"%b,"="+r"\mbox{}\hanzimibox"+"\n")
            neq+=1
    if neq>729:
        break

\end{pycode}

\end{multicols}
\end{document} 

结果为:

练习册

小结

本文利用pythontex调用python代码随机的生成算术计算式,并结合latex生成算术练习手册,并介绍了pythontex的功能和用法。 当然出于快速考虑,算术计算式没有做太多的优化,否则可以利用方格等方式将其对齐起来,但总的来说是已经不影响使用了。

参考:

  1. https://ctan.org/pkg/python

  2. https://ctan.org/pkg/pythontex

  3. pythontex_quickstart.pdf

  4. pythontex_gallery.pdf

  5. pythontex.pdf

暂无评论