LaTex: задачи для детей

7 сентября 2011 г.

Программка состоит из двух частей: шаблона на LaTeX-е и Perl-скрипта, наполняющего шаблон смыслом (т.е. числами). В текущем виде он работает в полу-ручном режиме — из Латех-файла PDF делаю руками (что в общем-то не сложно доделать); а разделил её на сам скрипт и шаблон для удобства добавления/изменения если что. Теперь скрипт…:

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
#!/usr/bin/perl
use feature qw(switch say);
use strict;
use Getopt::Std;
use Math::Random;
use Text::Iconv;
use locale;
# Ключи запуска
# -c количество примеров
# -r количество разрядов
# -a addition - сложение
# -s subtraction - вычитание
# -m multiplication - умножение
# -d division - деление
#
my $pash_tex = '/home/user/math/'; # Путь к временной папке LaTeX
our %options=();
if (not getopts("e:r:asmd",\%options)) {
 print STDERR "Внимание! Использованы неизвестные опции запуска - они проигнорированы!\n(Возможно, Вы ввели опции русскими символами)\n";
};
if (! (($options{a}) || ($options{s}) || ($options{m}) || ($options{d}))) {
 print STDERR "Необходимо указать хотя бы одно действие: +(-a),-(-s),*(-m),/(-d)\n";
 exit;
}
my $ranks; my $examples;
if (!$options{r}) { $ranks = 3 } # Кол-во разрядов
else { $ranks = $options{r} };
if (!$options{e}) { $examples = 40 } # Кол-во примеров
else { $examples = int(($options{e}+3)/4)*4 };
my $max_num = 10**$ranks;
my @ques_ans; # Массив вопросов и ответов
my $choice_action;
my $action;
for my $i (0..$examples-1) {
 $choice_action = 0;
 do {
 $action = int(rand 4);
 if ((($options{a}) && ($action == 0)) || (($options{s}) && ($action == 1)) ||
 (($options{m}) && ($action == 2)) || (($options{d}) && ($action == 3)))
 { $choice_action = 1; };
 } until ($choice_action == 1);
# print $action,"\n";
 if ($action == 0) { # Сложение
 $ques_ans[$i][3] = '+';
 my $fi = int(rand $max_num);
 my $se = int(rand $max_num);
 if ($fi > $se) { $ques_ans[$i][2] = $fi; $ques_ans[$i][0] = $se;
 } else { $ques_ans[$i][2] = $se; $ques_ans[$i][0] = $fi; }
 $ques_ans[$i][1] = $ques_ans[$i][2] - $ques_ans[$i][0];
 } elsif ($action == 1) { # Вычитание
 $ques_ans[$i][3] = '-';
 my $fi = int(rand $max_num);
 my $se = int(rand $max_num);
 if ($fi > $se) { $ques_ans[$i][0] = $fi; $ques_ans[$i][1] = $se;
 } else { $ques_ans[$i][0] = $se; $ques_ans[$i][1] = $fi; }
 $ques_ans[$i][2] = $ques_ans[$i][0] - $ques_ans[$i][1];
 } elsif (($action == 2) || ($action == 3)) { # Умножение
 $ques_ans[$i][3] = 'x';
 $choice_action = 0;
 do {
 my $fi = int(rand $max_num/3-2)+2;
 my $se = int(rand $max_num/3-2)+2;
 my $re;
 $fi>$se ? ($re = $fi/$se) : ($re = $se/$fi);
 if (($re < 50) && ( !( (($fi<10) || ($se<10)) && ($fi*$se>100)) || rand(5) )) {
 if ($fi*$se<$max_num*1.2 ) {
 $ques_ans[$i][0] = $fi;
 $ques_ans[$i][1] = $se;
 $ques_ans[$i][2] = $ques_ans[$i][0] * $ques_ans[$i][1];
 $choice_action = 1;
 };
 };
 } until ($choice_action == 1);
 if ($action == 3) {
 $ques_ans[$i][3] = '÷';
 ($ques_ans[$i][2],$ques_ans[$i][0]) = ($ques_ans[$i][0],$ques_ans[$i][2])
 };
 };
}
my $latex_out_file_name = $pash_tex."result.tex";
open LAYOUT, "<", $pash_tex."make_math.tex";
open LATEX, ">", $latex_out_file_name;
my @loop_str_prod=(); # массив строк в цикле примеров
my $mark_of_loop = 0; # маркер цикла примеров
while () { # Цикл считывания файла ШАБЛОНА .tex
 chomp;
 if ($mark_of_loop) { # в цикле считывания строчек примера - запоминать строки и искать конец одного примера
 if (m/^%#%\*/) { # если строка - команда
 my $ins_cmd = (split /:/)[1]; # найти имя команды
 if ($ins_cmd eq "end_one_cell") { # если последняя строка вывода ячейки - выводим примеры
 for my $all_rows (1..$examples/4){
 for my $all_cells (1..4){
 my $current_example = (($all_rows-1)*4+$all_cells-1); # номер текущего примера
 my $interval_resalt = 30; # интервал по умолчанию для сложения и вычитания
 if ($ques_ans[$current_example][3] eq 'x') {
 $interval_resalt = $interval_resalt + 20 * ((length($ques_ans[$current_example][1])-1) );
 } elsif ($ques_ans[$current_example][3] eq '÷') {
 $interval_resalt = $interval_resalt + 35 * ((length($ques_ans[$current_example][2])-1) );
 }
 if ($ques_ans[$current_example][3] ne '÷') {
 for my $i (0..$#loop_str_prod) { # для каждой tex-строки..
 if ($loop_str_prod[$i] =~ m/^%#%\*/) { # это команда?
 my $ins_cmd = (split /:/ , $loop_str_prod[$i])[1];
 if ($ins_cmd eq "number_question") { # Вставляем операцию
 print LATEX ($current_example+1),")";
 } elsif ($ins_cmd eq "operation") { # Вставляем операцию number_question
 print LATEX $ques_ans[$current_example][3];
 } elsif ($ins_cmd eq "first_arg") {
 print LATEX $ques_ans[$current_example][0];
 } elsif ($ins_cmd eq "second_arg") {
 print LATEX $ques_ans[$current_example][1];
 } elsif ($ins_cmd eq "interval_resalt") {
 print LATEX $interval_resalt;
 }
 # конец обработки команды interval_resalt
 } else { # если не команда просто вывести строку в файл
 say LATEX $loop_str_prod[$i];
 }
 } # Конец вывода одной ячейки примера (при +,-,*)
 } else {
 say LATEX "\\makebox[5mm][l]{", ($current_example+1),") }\n\\makebox[40mm]{\n\$\$\$\n\\begin{array}{r}\n\\begin{tabular}{r|l}\n\\texttt{\\Large",$ques_ans[$current_example][0],"} &\n\\texttt{\\Large", $ques_ans[$current_example][1],"}\\\\\n\\cline{2-2}\n\& \\rule{0pt}{",$interval_resalt,"pt} \\rule{",(length($ques_ans[$current_example][2])*10),"pt}{0pt}\\\\ \n \\end{tabular}\n\\end{array}\n\$\$\$\n}";
 }
 if ($all_cells == 4){
 say LATEX '\\\\ \\hline';
 } else {
 say LATEX '&';
 }
 } # Конец вывода строки
 } # Конец вывода всех примеров
 $mark_of_loop = 0;
 } else {
 push @loop_str_prod, $_;
 } # Конец вывода в tex-файл примеров
 } else { # если строка - не команда, то просто считать
 push @loop_str_prod, $_;
 }
 } elsif (m/^%#%\*/) { # не в цикле и это команда, то обрабатываем команду
 my $ins_cmd = (split /:/)[1];
 if ($ins_cmd eq "begin_one_cell") {
 $mark_of_loop = 1;
 } elsif ($ins_cmd eq "answer_page") {
 for my $i (1..$examples) {
 print LATEX $i,")~",$ques_ans[$i-1][2],"; ";
 }
 }
 } else {
 say LATEX $_;
 }
} # конец цикла считывания файла ШАБЛОНА .tex и формирования выходного tex-файла
close LAYOUT;
close LATEX;
exit;

… и шаблон (сохранить в make_math.tex для соответствия скрипту):

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
\documentclass[a4paper,12pt,oneside]{article}
\usepackage{multirow}
\usepackage{makecell}
\usepackage[utf8]{inputenc}
\usepackage[english,russian]{babel}
\usepackage{longtable}
% Page layout (geometry)
\setlength\voffset{-1.3in} %{-15.4mm}
\setlength\hoffset{-1.3in}
\setlength\oddsidemargin{2cm}
\setlength\textheight{267mm}
\setlength\textwidth{180mm}
\setlength\marginparwidth{8mm}
\setlength\marginparsep{2mm}
\setlength\footskip{1.0cm}
\begin{document}
%\pagestyle{empty}
\begin{longtable}{p{40mm}|p{40mm}|p{40mm}|p{40mm}}
%#%*:begin_one_cell: ==== начало одной ячейки ====
\makebox[5mm][l]{
%#%*:number_question: ==== номер примера ====
}
\makebox[40mm][r]{
$$$
\begin{array}{r}
\texttt{\Large
%#%*:operation: ==== действие ====
}
\begin{array}{r}
\texttt{\Large
%#%*:first_arg: ==== первый аргумент ====
}\\
\texttt{\Large
%#%*:second_arg: ==== второй аргумент ====
}\\
\end{array} \\
\hline
\begin{array}{r}
\rule{0pt}{
%#%*:interval_resalt: ==== интервал для результата ====
pt}
\end{array}
\end{array}
$$$
}
%#%*:end_one_cell: ==== конец одной ячейки ====
% после каждой ячейки в программе нужно добавлять "&" и после строки (4-ре ячейки) - "\\"
\end{longtable}
\newpage
%#%*:answer_page: ==== страница ответов ====
\end{document}

Как водится привел суть программки, опустив, возможно, необходимые проверки. Теперь по логике работы в двух словах.

Результат выводится строками по 4-ре примера в каждой на формате A4. Задается в командной строке кол-во разрядов и кол-во примеров (которое округляется до кратного 4-ке числа), выбираются случайным образом операнды и дальше идет процесс вывода в латех-файл. Но в первой части я так и не сделал нормальное формирование операндов при умножении/делении – не нравится оно мне как-то (оно то работает правильно, но вот насколько равномерно-случайный выбор идет – это вопрос).

Теперь о выводе в латеховский файл. Думаю понятно что вывожу меняющиеся параметры (операнды и действие) в цикле, добавляя строки между “начало ячейки” и “конец ячейки”. А вот втиснуть в эту схему “деление” не удалось, уж очень у него отличающийся внешний вид – поэтому пришлось втиснуть латеховский код при делении в сам скрипт.

Результат получается такой:
LaTex пример

Теги:
рубрика Программирование
  • Похожие статьи
  • Предыдущие из рубрики