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

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

Программка состоит из двух частей: шаблона на 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-ке числа), выбираются случайным образом операнды и дальше идет процесс вывода в латех-файл. Но в первой части я так и не сделал нормальное формирование операндов при умножении/делении – не нравится оно мне как-то (оно то работает правильно, но вот насколько равномерно-случайный выбор идет – это вопрос).

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

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

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