LaTex: задачи для детей
Программка состоит из двух частей: шаблона на LaTeX-е и Perl-скрипта, наполняющего шаблон смыслом (т.е. числами). В текущем виде он работает в полу-ручном режиме — из Латех-файла PDF делаю руками (что в общем-то не сложно доделать); а разделил её на сам скрипт и шаблон для удобства добавления/изменения если что. Теперь скрипт…:
#!/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 для соответствия скрипту):
\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-ке числа), выбираются случайным образом операнды и дальше идет процесс вывода в латех-файл. Но в первой части я так и не сделал нормальное формирование операндов при умножении/делении – не нравится оно мне как-то (оно то работает правильно, но вот насколько равномерно-случайный выбор идет – это вопрос).
Теперь о выводе в латеховский файл. Думаю понятно что вывожу меняющиеся параметры (операнды и действие) в цикле, добавляя строки между “начало ячейки” и “конец ячейки”. А вот втиснуть в эту схему “деление” не удалось, уж очень у него отличающийся внешний вид – поэтому пришлось втиснуть латеховский код при делении в сам скрипт.