Pull to refresh

LaTex: Упражнения продолжаются или как «замучать» своих детей

Наткнувшись недавно на топик LaTex: Упражнение я решил, что мне такое тоже надо. Но поскольку с Groovy разбираться мне не очень хотелось, а добавить действия в дополнение к сложению надо, я решил переписать скрипт с нуля.
Вот что у меня получилось.

Начну я с краткого пояснения. Программка состоит из двух частей: шаблона на 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][c]{\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-ке числа), выбираются случайным образом операнды и дальше идет процесс вывода в латех-файл. Но в первой части я так и не сделал нормальное формирование операндов при умножении/делении - не нравится оно мне как-то (оно то работает правильно, но вот насколько равномерно-случайный выбор идет - это вопрос).
Теперь о выводе в латеховский файл. Думаю понятно что вывожу меняющиеся параметры (операнды и действие) в цикле, добавляя строки между "начало ячейки" и "конец ячейки". А вот втиснуть в эту схему "деление" не удалось, уж очень у него отличающийся внешний вид - поэтому пришлось втиснуть латеховский код при делении в сам скрипт.
Результат получается такой:
image

Собственно все, пользуйтесь на радость детям, высказывайте замечания и предложения.

P.S. как оформить код с отступами для правильночитаемости я так и не понял, пробелы и табуляции игнорировались - прошу простить.
Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.