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-21 05:04 pm (UTC)
From: [identity profile] asviraspossible.livejournal.com
Я вот всё пытаюсь путём таких последовательных преобразований перейти от event-driven programming к FRP, и пока как-то не очень удачно...

Date: 2010-02-22 08:47 am (UTC)
From: [identity profile] deni-ok.livejournal.com
У меня как-то руки до FRP никак не дойдут :(

Date: 2010-02-21 05:50 pm (UTC)
From: [identity profile] ivan-gandhi.livejournal.com
Если подойти философски, то и в джаве то же самое надо делать. Для четырёх я бы и не задумался.

Но, конечно, в хаскеле всё выразительней.

Смешно как хаскельный flip напоминает фортовский swap.

Date: 2010-02-21 06:43 pm (UTC)
From: [identity profile] asviraspossible.livejournal.com
Я кстати всегда пишу flip префиксно, почёркивая, что flip f --- эта некая новая функция, только с аргументами наоборот:

(flip f) x y == f y x

Date: 2010-02-21 08:29 pm (UTC)
From: [identity profile] alexey-rom.livejournal.com
А я бы вместо (openFile `flip` AppendMode) написал (`openFile` AppendMode). По-моему, проще.

Date: 2010-02-21 09:18 pm (UTC)
From: [identity profile] voidex.livejournal.com
Причём везде.

f `flip` x === `f` x

Date: 2010-02-21 09:30 pm (UTC)
From: [identity profile] deni-ok.livejournal.com
Ага.
А === означает тождественно-претождественно? :-)

Date: 2010-02-21 10:31 pm (UTC)
From: [identity profile] voidex.livejournal.com
Тождественнее некуда
Просто (==) уже есть :)

Date: 2010-02-22 08:38 am (UTC)
From: [identity profile] zhacka (from livejournal.com)
Не лучше ли было бы заменить (replicate 4 AppendMode) и подобные вещи на что-то типа (repeat AppendMode)? :) А то хардкодить 4-ку как-то не очень эстетично. :)

Date: 2010-02-22 08:39 am (UTC)
From: [identity profile] zhacka (from livejournal.com)
:-) Извините, просто ускользнуло от моих глаз. Не внимательно читал.

Date: 2010-02-22 08:46 am (UTC)
From: [identity profile] deni-ok.livejournal.com
Не стоит извиняться, всё правильно; пост про рефакторинг и должен будить соответствующий зуд улучшательства :-)

Date: 2010-02-22 08:50 am (UTC)
From: [identity profile] zhacka (from livejournal.com)
На самом деле, приведенное в конце решение оказалось еще более изящным и эстетичным. По этому, замена replicate на repeat уже не актуальна.

(offtop)

Date: 2010-02-22 12:28 pm (UTC)
From: [identity profile] asviraspossible.livejournal.com
А вы на GHC 6.12 под Windows пишете? Там нормально всё работает с вводом-выводом по русски? И там библиотеки динамически подгружаются или всё таки статично всё линкуется? И зачем вам BOM нужен? :)

Re: (offtop)

Date: 2010-02-22 01:42 pm (UTC)
From: [identity profile] deni-ok.livejournal.com
(1) Угу, решил вот попробовать 6.12, как раз из-за развитых средств для Unicode-aware Handles:
http://www.haskell.org/pipermail/cvs-libraries/2009-June/010890.html
(2,4) C UTF8 - нормально. Я принял решение прекратить работать под Windows с CP1251 и перейти на UTF8, где это только возможно (а возможно это в подавляющем большинстве нужных мне инструментов). А BOM мне нужен, чтобы всякий тупняк типа notepad.exe открывал всё в правильной кодировке.
(3) И не смотрел

Date: 2010-02-22 01:12 pm (UTC)
From: [identity profile] vanja-y.livejournal.com
Так как с каждым файлом производится только одна операция можно воспользоваться функцией
appendFile :: FilePath -> String -> IO()

Можно еще автоматизировать создание названий для файлов с результатами. Тогда получится что-то вроде

appendCustom :: String -> FilePath -> [Ext] -> IO()
appendCustom s f l = sequence_ [ appendFile (f ++ "." ++ e) 
                                   (genText e s) | e<-l]

где Ext синоним для String и genText предполагается слегка переделаной. Осталось скомбинировать appendCustom с replicateM:

main = appendCustom (replicateM 30 gen_pd) "res" ["txt","tex","answ.txt","dat.cs"] 

Date: 2010-02-22 01:48 pm (UTC)
From: [identity profile] deni-ok.livejournal.com
Эй, у меня были специализированные генераторы заданий для каждого типа файла! :-)

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. 28th, 2025 07:06 pm
Powered by Dreamwidth Studios