deniok: (Default)
[personal profile] deniok
Писал тут код для генерации однотипных задачек; результат должен был отправляться в TeX'овский файл. По ходу дела прибежали дополнительные требования. Сначала попросили, чтобы ответы жили в отдельном файле, потом чисто текстовую версию, и, наконец, коэффициенты в csv. В итоге, поскольку все усилия были направлены на содержательную генерацию, в IO части получился ужасный копипастный монстр:

-- исходная версия
doLinearProg fileTXT fileTeX fileAns fileDat = do 
   -- создали/открыли файлы
   handleTXT <- openFile fileTXT AppendMode
   handleTeX <- openFile fileTeX AppendMode 
   handleAns <- openFile fileAns AppendMode 
   handleDat <- openFile fileDat AppendMode 
   -- установили кодировку
   hSetEncoding handleTXT utf8_bom
   hSetEncoding handleTeX utf8_bom
   hSetEncoding handleAns utf8_bom
   hSetEncoding handleDat utf8_bom
   -- нагенерили список из 30 случайно-неслучайных структур
   lst <- replicateM 30 gen_pd
   -- забацали на основе каждой структуры текст задачи (taskXXX) 
   -- и склеили все 30 задач в один текст с нумерацией (genText)
   let txt = genText taskTXT lst
   let tex = genText taskTeX lst
   let ans = genText answ lst
   let dat = genText taskDAT lst
   -- записали в файлы
   hPutStr handleTXT txt
   hPutStr handleTeX tex
   hPutStr handleAns ans
   hPutStr handleDat dat
   -- закрыли хэндлы
   hClose handleTXT
   hClose handleTeX
   hClose handleAns
   hClose handleDat


Поглядел я на всё это, и понял, что дело дрянь. Дав волю бунтующему эстетическому чувству, свернул каждую четверку в один вызов через списочно-монадические функции:
doLinearProg fileTXT fileTeX fileAns fileDat = do 
   handles <- zipWithM openFile [fileTXT, fileTeX, fileAns, fileDat]
                                (replicate 4 AppendMode)
   zipWithM hSetEncoding handles
                         (replicate 4 utf8_bom)
   lst <- replicateM 30 gen_pd
   let txts = zipWith genText [taskTXT,taskTeX,answ,taskDAT]
                              (replicate 4 lst)
   zipWithM hPutStr handles txts
   mapM hClose handles
А потом ещё чуть-чуть поморщился и избавился от дурацких replicate 4:
doLinearProg fileTXT fileTeX fileAns fileDat = do 
   handles <- mapM (openFile `flip` AppendMode)
                   [fileTXT, fileTeX, fileAns, fileDat]
   mapM (hSetEncoding `flip` utf8_bom) handles
   lst <- replicateM 30 gen_pd
   let txts = map (genText `flip` lst) 
                  [taskTXT, taskTeX, answ, taskDAT]
   zipWithM hPutStr handles txts
   mapM hClose handles
Использование flip для частичного применения не первого, а второго аргумента функции -- полезная в хозяйстве идиома:
f `flip` y = \x -> f x y

Теперь видна следующая проблема: число 4, как длина списков. Если приедут требования дополнительного выходного формата, придется менять код в трёх местах, неочевидным образом связанных друг с другом. Не лучше ли передавать пары (имя файла, функция-генератор содержимого для него) в нужном количестве, то есть использовать в качестве аргументов функции список таких пар? Сказано -- сделано:
doLinearProg fNmsCGnsPairs = do 
   let fileNames = map fst fNmsCGnsPairs
   let contentGens = map snd fNmsCGnsPairs
   handles <- mapM (openFile `flip` AppendMode) fileNames
   mapM (hSetEncoding `flip` utf8_bom) handles
   lst <- replicateM 30 gen_pd
   let txts = map (genText `flip` lst) contentGens
   zipWithM hPutStr handles txts
   mapM hClose handles
Вы можете сделать мне замечание насчет числа 30. Я с ним полностью согласен, это число вытащено в окончательной версии в качестве параметра doLinearProg. Вызов её в итоге выглядит так
main = doLinearProg 30 
   [("res.txt", taskTXT),
    ("res.tex", taskTeX),
    ("res_answ.txt", answ),
    ("res_dat.csv", taskDAT)]

Date: 2010-02-22 01:59 pm (UTC)
From: [identity profile] vanja-y.livejournal.com
А что genText делает? Я подумал, что taskXXX просто элементы дополнительного типа, которые определяют, что будет делать genText.

Date: 2010-02-22 03:28 pm (UTC)
From: [identity profile] deni-ok.livejournal.com
taskXXX как раз формулируют задачу в виде специфической строки (либо в TeX отформатировано, либо в ином виде). А genText напускает конкретный taskXXX на список подготовленных случайных структур с данными, а потом ставит нумерацию, переводы строк, склеивая в итоге всё в одну строку.

Profile

deniok: (Default)
deniok

February 2022

S M T W T F S
  12345
6789101112
13141516171819
20212223 242526
2728     

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Jun. 30th, 2025 12:38 pm
Powered by Dreamwidth Studios