Мастер-класс по ФП
От: BulatZiganshin  
Дата: 31.12.08 14:46
Оценка: 6 (1)
я часто сталкиваюсь с двумя точками зрения — первая "я знаком с ФП, но оно никакой выгоды в программировании не даёт", и вторая "я знаком с ФП, но так и не научился использовать его для повышения эффективности программирования". собственно, это одно и то же, разница только в том, на кого (ФП или самого человека) возлагается вина за отсутствие результата

и поскольку этот разрыв можно преодолеть, только решая практические задачи, я предлагаю устроить мастер-класс, где опытные фп-программисты будут демонстрировать фп-подход к решению различных практических задач. для того, чтобы можно было ценить насколько вы освоили фп-подход, и оценить его преимущества по сравнению с традиционным, я предлагаю начинать с попыток "учеников" самим решить предлагаемые задачи, и затем демонстрации "учителями" их решений

итак, попробуйте решить эти задачи:

1) есть список слов. надо записать в файл "a" все слова, начинающиеся на букву 'a', в файл "b" — слова на 'b' и т.д. вплоть до z

2) имеется список имён файлов в архиве:

dir1\file1
dir1\dir2\file2
dir1\dir3\file3
dir4\file4

требуется написать функцию, которая по имени каталога возвращает список файлов и подкаталогов в нём, к примеру

f "dir1" = (["file1"], ["dir2","dir3"])

a) сложностью O(кол-во файлов)
б) более эффективную
Люди, я люблю вас! Будьте бдительны!!!
Re: Мастер-класс по ФП
От: Гест Украина https://zverok.github.io
Дата: 31.12.08 15:29
Оценка: 1 (1)
Здравствуйте, BulatZiganshin, Вы писали:

BZ>я часто сталкиваюсь с двумя точками зрения — первая "я знаком с ФП, но оно никакой выгоды в программировании не даёт", и вторая "я знаком с ФП, но так и не научился использовать его для повышения эффективности программирования". собственно, это одно и то же, разница только в том, на кого (ФП или самого человека) возлагается вина за отсутствие результата


BZ>и поскольку этот разрыв можно преодолеть, только решая практические задачи, я предлагаю устроить мастер-класс, где опытные фп-программисты будут демонстрировать фп-подход к решению различных практических задач. для того, чтобы можно было ценить насколько вы освоили фп-подход, и оценить его преимущества по сравнению с традиционным, я предлагаю начинать с попыток "учеников" самим решить предлагаемые задачи, и затем демонстрации "учителями" их решений


Любопытно. Только у меня несколько другой вариант "ученичества" — уж не знаю, готовы ли "учителя" иметь с этим дело: язык общего назначения с некоторыми функциональными возможностями, и мне интересно, насколько функционален используемый мной подход к задаче.

BZ>итак, попробуйте решить эти задачи:


BZ>1) есть список слов. надо записать в файл "a" все слова, начинающиеся на букву 'a', в файл "b" — слова на 'b' и т.д. вплоть до z


words.
    group_by{|word| word[0]}.
    each{|letter, group| File.open(letter, 'w').write(group.join("\n"))}


BZ>2) имеется список имён файлов в архиве:


BZ>dir1\file1

BZ>dir1\dir2\file2
BZ>dir1\dir3\file3
BZ>dir4\file4

BZ>требуется написать функцию, которая по имени каталога возвращает список файлов и подкаталогов в нём, к примеру


BZ>f "dir1" = (["file1"], ["dir2","dir3"])


BZ>a) сложностью O(кол-во файлов)

def dir_contents(dir)
  pathes.
    select{|path| in_dir?(path, dir)}.
    map{|path| first_path_element(relative_path(path, dir))}.
    uniq.
    partition{|path| is_dir?(path)}
end

# вспомогательные функции могут выглядеть так (а могут, например, работать с объектами файловой системы)
def in_dir?(path, dir)
  path.index(dir) == 0
end

def is_dir?(path)
    path =~ /\\$/
end

def relative_path(path, relative_to)
    path.sub(/^#{relative_to}\\/, '')
end

def first_path_element(path)
    path.sub(/^(\w+?)(?:(\\)\w*)?$/, '\1\2')
end


BZ>б) более эффективную


Re[2]: Мастер-класс по ФП
От: BulatZiganshin  
Дата: 31.12.08 17:28
Оценка:
Здравствуйте, Гест, Вы писали:

Г>Любопытно. Только у меня несколько другой вариант "ученичества" — уж не знаю, готовы ли "учителя" иметь с этим дело: язык общего назначения с некоторыми функциональными возможностями, и мне интересно, насколько функционален используемый мной подход к задаче.


да, более чем, и решения кстати у тебя совершенно fp-шные. смысл тренинга в том, чтобы принести вам пользу, и я тут вижу два "фронта" — сравнить фп и императивные решения на "рабочих" языках, и сравнить удобство фп-программирования на специализированных (хаскелл/окамл/...) и обычных языках. так что пишите так как для вас естественней. в конечном счёте, я надеюсь, вы получите навык использования фп-языков на все 100, и навык использования фп-подходов к решению задач в обычных языках тоже

BZ>>б) более эффективную


Г>


имеется в виду разумеется построение промежуточной структуры данных y, которое позволит далее вычислять dir file y за время меньше O(N)


3-я задача: есть функция сжатия compress :: String -> String. ей на вход передаётся строка (или буфер и его размер — это непринципиально), а она возвращает сжатые данные. и есть аналогичная функция decompress. при этом они однопоточные. а у нас 4-процессорная машина. соответственно, нужно написать программу, читающую входные данные мегабайтными блоками, и упаковывающую "в 4 руки", и к ней программу распаковки
Люди, я люблю вас! Будьте бдительны!!!
Re[3]: Мастер-класс по ФП
От: Гест Украина https://zverok.github.io
Дата: 31.12.08 18:09
Оценка: 1 (1)
Здравствуйте, BulatZiganshin, Вы писали:

BZ>>>б) более эффективную


Г>>


BZ>имеется в виду разумеется построение промежуточной структуры данных y, которое позволит далее вычислять dir file y за время меньше O(N)


А, понял. Я подумал про промежуточную структуру, но решил что это выходит за рамки задачи.
Что приходит в голову сходу — это, собственно, сразу построить дерево папок:
        |dir2 => file2
dir1 => |dir3 => file3
        |file1
dir4 => file4


Тогда сложность ее будет, если не ошибаюсь, O(M), где M — кол-во папок верхнего уровня.
Типа так:
dirlist = pathes.
  group_by{|path| first_path_element(path)}.                        #складываем по папкам
  map{|dir, group| [dir, group.map{|path| relative(path, dir)}]} #откусываем имя папки, в которую положили

def dir_contents(dir)
  res = dirlist.detect{|d, path| d == dir}
  res && res.last || [] #last - в смысле, из массива [папка, [содержимое папки]]
end


Более разумный вариант — превратить dirlist в словарь (папка — содержимое). Тогда сложность поиска, естественно, определяется сложностью поиска по словарю в целеовм языке.
dirlist_hash = dirlist.to_hash

def dir_contents(dir)
  dirlist_hash[dir]
end


наверное, можно как-то умнее.


BZ>3-я задача: есть функция сжатия compress :: String -> String. ей на вход передаётся строка (или буфер и его размер — это непринципиально), а она возвращает сжатые данные. и есть аналогичная функция decompress. при этом они однопоточные. а у нас 4-процессорная машина. соответственно, нужно написать программу, читающую входные данные мегабайтными блоками, и упаковывающую "в 4 руки", и к ней программу распаковки


Мммм...

AMOUNT = 1024 * 1024

#исхожу из того, что stream.read сдвигает указатель начала входного потока
def compress4(stream)
  stream.
    #превращаем поток в массив массивов из 4х элементов по 1 Мб
    unfold{|stream| stream.empty? ? nil : [(1..4).map{stream.read(AMOUNT)}, stream]}. 
    #сжимаем, исходя из того, что map у нас достаточно умен для распараллеливания ;)
    #и сразу склеиваем 4 сжатых кусочка в 1
    map{|four_chunks| four_chunks.map{|chunk| compress(chunk)}.join}.
    join        #...и склеиваем все
end


Тут, правда, наверное, надо оговорить, что простое "склеивание" сжатых кусочков может дать абсолютно некорректный результат, в зависимости от алгоритмов сжатия

Разжатие не пишу — кажется, будет абсолютно то же самое, только compress надо заменить на decompress
Re[4]: Мастер-класс по ФП
От: BulatZiganshin  
Дата: 31.12.08 18:50
Оценка:
Здравствуйте, Гест, Вы писали:

Г>А, понял. Я подумал про промежуточную структуру, но решил что это выходит за рамки задачи.


ну понятно же, что меньше O(N) на самом списке не получишь

Г>Что приходит в голову сходу — это, собственно, сразу построить дерево папок:

Г>
Г>        |dir2 => file2
Г>dir1 => |dir3 => file3
Г>        |file1
Г>dir4 => file4
Г>


Г>Тогда сложность ее будет, если не ошибаюсь, O(M), где M — кол-во папок верхнего уровня.

Г>Типа так:
Г>
Г>dirlist = pathes.
Г>  group_by{|path| first_path_element(path)}.                        #складываем по папкам
Г>  map{|dir, group| [dir, group.map{|path| relative(path, dir)}]} #откусываем имя папки, в которую положили

результат будет что-то типа такого:

[("dir1", ["dir2/file2", "dir3/file3", "file1"]), ...)


Г>  res && res.last || [] #last - в смысле, из массива [папка, [содержимое папки]]

а не лучше писать "res? res.last : []" ?  кстати path во множ. числе тоже вроде paths :)

Г>Более разумный вариант - превратить dirlist в словарь (папка - содержимое). Тогда сложность поиска, естественно, определяется сложностью поиска по словарю в целеовм языке.

ага. но для этого надо построить другой dirlist, нежели дерево

Г>#исхожу из того, что stream.read сдвигает указатель начала входного потока
Г>def compress4(stream)
Г>  stream.
Г>    #превращаем поток в массив массивов из 4х элементов по 1 Мб
Г>    unfold{|stream| stream.empty? ? nil : [(1..4).map{stream.read(AMOUNT)}, stream]}. 
Г>    #сжимаем, исходя из того, что map у нас достаточно умен для распараллеливания ;)
Г>    #и сразу склеиваем 4 сжатых кусочка в 1
Г>    map{|four_chunks| four_chunks.map{|chunk| compress(chunk)}.join}.
Г>    join        #...и склеиваем все
Г>end
Г>


вот и напишите этот map в общем, это была задача именно на высокоуровневый подход к многозадачности. ваше решение мне нравится — именно в fp-шном стиле декомпозиция задачи на отдельные элементы плюс подход в стиле "обработка потока данных"
Люди, я люблю вас! Будьте бдительны!!!
Re[5]: Мастер-класс по ФП
От: Гест Украина https://zverok.github.io
Дата: 31.12.08 19:00
Оценка:
Здравствуйте, BulatZiganshin, Вы писали:

Г>>Типа так:

Г>>
Г>>dirlist = pathes.
Г>>  group_by{|path| first_path_element(path)}.                        #складываем по папкам
Г>>  map{|dir, group| [dir, group.map{|path| relative(path, dir)}]} #откусываем имя папки, в которую положили

BZ>результат будет что-то типа такого:

BZ>[("dir1", ["dir2/file2", "dir3/file3", "file1"]), ...)

Ну да.

Г>>  res && res.last || [] #last - в смысле, из массива [папка, [содержимое папки]]

BZ>а не лучше писать "res? res.last : []" ?  кстати path во множ. числе тоже вроде paths :)

Угу.

Г>>Более разумный вариант - превратить dirlist в словарь (папка - содержимое). Тогда сложность поиска, естественно, определяется сложностью поиска по словарю в целеовм языке.

BZ>ага. но для этого надо построить другой dirlist, нежели дерево

? почему другой? вот прямо этот [ [dir, contents], ...] отлично превращается в словарь.

Г>>#исхожу из того, что stream.read сдвигает указатель начала входного потока
Г>>def compress4(stream)
Г>>  stream.
Г>>    #превращаем поток в массив массивов из 4х элементов по 1 Мб
Г>>    unfold{|stream| stream.empty? ? nil : [(1..4).map{stream.read(AMOUNT)}, stream]}. 
Г>>    #сжимаем, исходя из того, что map у нас достаточно умен для распараллеливания ;)
Г>>    #и сразу склеиваем 4 сжатых кусочка в 1
Г>>    map{|four_chunks| four_chunks.map{|chunk| compress(chunk)}.join}.
Г>>    join        #...и склеиваем все
Г>>end
Г>>


BZ>вот и напишите этот map


Ой нет. Эт я даже не представляю, с чего начинать. В смысле, если писать на Рубях, нужна видимо какая-то библиотека для параллельности, а я такими вопросами не задавался
Re[6]: Мастер-класс по ФП
От: BulatZiganshin  
Дата: 31.12.08 19:08
Оценка:
Здравствуйте, Гест, Вы писали:

BZ>>результат будет что-то типа такого:


BZ>>[("dir1", ["dir2/file2", "dir3/file3", "file1"]), ...)


Г>Ну да.


нам нужно, чтобы
1) f "dir1" возвращало "file1", "dir2", "dir3" вне зависмости от кол-ва фалйов в dir2/dir3
2) f "dir1/dir2" возвращало "file2"
Люди, я люблю вас! Будьте бдительны!!!
Re: Мастер-класс по ФП
От: geniepro http://geniepro.livejournal.com/
Дата: 31.12.08 21:14
Оценка:
Здравствуйте, BulatZiganshin, Вы писали:

BZ> итак, попробуйте решить эти задачи:

BZ> 1) есть список слов. надо записать в файл "a" все слова, начинающиеся на букву 'a', в файл "b" — слова на 'b' и т.д. вплоть до z
import Char
import List

main = do
    ls <- readFile "word.txt"

    let fs = words ls
          |> sortBy  (\(x:_) (y:_) -> toLower x `compare` toLower y)
          |> groupBy (\(x:_) (y:_) -> x == y)
          |> filter  (\((x:_):_)   -> isAlpha x)

    mapM_ (\f@((c:_):_) -> writeFile [c] $ unlines f) fs


infixl 0 |>
x |> f = f x


BZ> 2) имеется список имён файлов в архиве:

BZ> dir1\file1
BZ> dir1\dir2\file2
BZ> dir1\dir3\file3
BZ> dir4\file4
BZ> требуется написать функцию, которая по имени каталога возвращает список файлов и подкаталогов в нём, к примеру
BZ> f "dir1" = (["file1"], ["dir2","dir3"])

Не совсем понятно условие. Должен ли просмотр идти по дереву каталогов и для запроса f "dir1" выдать (["file2"], []) или же можно ограничиться просмотром имён каталогов верхнего уровня?
Если второе, то вот:
import List

dirs = [ "dir1\\file1"
       , "dir1\\dir2\\file2"
       , "dir1\\dir3\\file3"
       , "dir4\\file4"
       ]

slash2tab c = if c == '\\' then "\\\t" else [c]

dirs' = map (\d -> words $ (concat $ map slash2tab d)) dirs

f'a dir = filter (\(d:_) -> init d == dir) dirs'
       |> map tail
       |> (\xs -> ( concat $  filter (\(x:_) -> last x /= '\\') xs
                  , filter (\(x:_) -> last x == '\\') xs
                    |> map (init.head)))


infixl 0 |>
x |> f = f x


PS. За изменениями условия задачи прямо не поспеешь... :о(
Re[3]: Мастер-класс по ФП
От: geniepro http://geniepro.livejournal.com/
Дата: 31.12.08 21:18
Оценка:
Здравствуйте, BulatZiganshin, Вы писали:

BZ>имеется в виду разумеется построение промежуточной структуры данных y, которое позволит далее вычислять dir file y за время меньше O(N)


Так у преобразования в такую структуру сложность всё равно O(кол-во файлов)...
Re[2]: Мастер-класс по ФП
От: geniepro http://geniepro.livejournal.com/
Дата: 31.12.08 21:21
Оценка:
Здравствуйте, geniepro, Вы писали:

> ... и для запроса f "dir1" выдать (["file2"], []) ...


имел в виду "для запроса f "dir2" выдать (["file2"], [])", впрочем, похоже, уже не важно... :о)
Re: Мастер-класс по ФП
От: Partisan  
Дата: 01.01.09 09:59
Оценка: +2
Здравствуйте, BulatZiganshin, Вы писали:



BZ>и поскольку этот разрыв можно преодолеть, только решая практические задачи, я предлагаю устроить мастер-класс, где опытные фп-программисты будут демонстрировать фп-подход к решению различных практических задач. для того, чтобы можно было ценить насколько вы освоили фп-подход, и оценить его преимущества по сравнению с традиционным, я предлагаю начинать с попыток "учеников" самим решить предлагаемые задачи, и затем демонстрации "учителями" их решений


"Решение практических задач" мне кажется — это составление программы, которая делает что-то полезное. Поэтому предлагаемые задачи не кажутся практическими. Скорее они всего лишь демонстрируют, что на каком-то языке ФП можно записать в 5 строчках то, что в простом Си заняло бы 10 строчек. Естественно, что это не доказывает преимущество ФП, даже если считать строчки — в простой задаче эта разница просто не имеет значения,а стоит задаче стать немного нетривиальной, как обнаружится слабость стандартной библиотеки (в терминологии Си) языков ФП по сравнению с нормальными, что плохо повлияет на количество строчек.
Кстати, в Си решение обеих задач тривиально (хотя код для поиска файлов зависит от платформы). Если функциональщикам эти задачи кажутся нетривиальными, то это тоже заставляет усомниться в полезности ФП.
Re: Мастер-класс по ФП
От: BulatZiganshin  
Дата: 01.01.09 10:03
Оценка:
Здравствуйте, BulatZiganshin, Вы писали:

BZ>2) имеется список имён файлов в архиве:


в этой задаче я подразумевал много вещей, которые оказались неочевидны. итак, уточнённая формулировка:

имеется список имён файлов в архиве:
dir1\file1
dir1\dir2\file2
dir1\dir3\file3
dir4\file4


мы пишем программу типа far/tc, которая позволит ходить по каталогам такого архива. для этого нужно написать функцию, возвращающую список файлов/каталогов в любом каталоге:
f ""          = ([],        ["dir1","dir4"])
f "dir1"      = (["file1"], ["dir2","dir3"])
f "dir1\dir2" = (["file2"], [])


a) сложностью O(кол-во файлов), манипулирующую непосредственно с исходным списком
б) более эффективную, манипулирующую промежуточной структурой данных, которую мы строим из списка. т.е. в этом случае мы при входе в архив строим некое эффективное представление его каталога, которое позволяет нам в дальнейшем быстро ходить по архиву
Люди, я люблю вас! Будьте бдительны!!!
Re[2]: Мастер-класс по ФП
От: dmz Россия  
Дата: 01.01.09 10:33
Оценка:
P>Кстати, в Си решение обеих задач тривиально (хотя код для поиска файлов зависит от платформы).

Ну так можно привести тривиальное сишное решение хотя бы первой задачи?

P>а стоит задаче стать немного нетривиальной, как обнаружится слабость стандартной библиотеки (в терминологии Си) языков ФП по сравнению с нормальными, P>что плохо повлияет на количество строчек.


Про слабость стандартной библиотеки, например, хаскелла или окамла — это откуда мысль? Может быть, вы ее подтвердите?

Решение первой задачи для окамла — как-то несколько многословно. Если кто-то знает окамл — может найдется лучшее решение?

open Std
open ExtList.List
open ExtString

(* group -  нигде нет *)
let rec group f l = match l with
     | x::xs -> let m,n = partition (fun y -> (f x) = (f y)) xs in [x :: m] @ group f n
     | []    -> []

let words = 
    (* isAlpha тоже нигде нет *)
    let filt  x = let good = function 
        | 'a' .. 'z' | 'A' .. 'Z' -> true
        | _                       -> false
        in (filter (fun a -> a <> "" &&  good a.[0]) x) in
    let read    = input_list stdin
    in group (fun x -> x.[0]) (filt read)

let _ = iter ( fun x -> output_file (String.sub (hd x) 0 1) (String.join "\n" (x)) ) words


dmz@x200:~/prj/ocaml/shit$ wc -l ./filez 
67406 ./filez
dmz@x200:~/prj/ocaml/shit$ time cat filez  | ./a.out 

real    0m0.201s
user    0m0.176s
sys    0m0.028s
Re[3]: Мастер-класс по ФП
От: D. Mon Великобритания http://thedeemon.livejournal.com
Дата: 01.01.09 12:15
Оценка: 12 (1)
Мой вариант первой задачи на Окамле. Не слишком красиво, зато линейная сложность (никаких сортировок).

let (|>) x f = f x;;

let words = ["Not"; "so"; "long"; "list"; "of"; "9-10"; "not"; "so"; "weird"; "words"];;

let idx s =
  if s<>"" then
    if s.[0]>='A' && s.[0]<='Z' then Some( (int_of_char s.[0]) - 65 ) else
    if s.[0]>='a' && s.[0]<='z' then Some( (int_of_char s.[0]) - 97 ) else None
  else None;;
  
let group lst =
  let a = Array.make 26 [] in
  List.iter (fun s-> match idx s with Some i -> a.(i) <- s::a.(i) | None -> ()) lst; a;;
  
let write =
  Array.iteri (fun i lst -> if lst <> [] then 
    Std.output_file ~filename:(i+97 |> char_of_int |> Std.string_of_char) 
                    ~text:(String.concat "\n" (List.rev lst)));;

words |> group |> write;;
Re[3]: Мастер-класс по ФП
От: Basil B Россия  
Дата: 01.01.09 15:14
Оценка:
Здравствуйте, dmz, Вы писали:

dmz>Ну так можно привести тривиальное сишное решение хотя бы первой задачи?


На плюсах.
Вариант №1. Do not optimize prematurely...
vector<string> words;

for(int i = 0, end = words.size(); i != end; ++i)
{
   ofstream(words[i].substr(0,1).c_str(), ios::app) << words[i] << ' ';
}


Вариант №2.


ofstream* ofs[26];

for(char c = 'a'; c <= 'z'; ++c)
{
    ofs[c - 'a'] =  new ofstream(string(1, c).c_str());
}

vector<string> words;

for(int i = 0, end = words.size(); i != end; ++i)
{
    *ofs[words[i][0] - 'a'] << words[i] << ' ';
}
Re[4]: Мастер-класс по ФП
От: BulatZiganshin  
Дата: 01.01.09 15:21
Оценка:
Здравствуйте, Basil B, Вы писали:

dmz>>Ну так можно привести тривиальное сишное решение хотя бы первой задачи?


отлично. а теперь условия задачи меняются — имя файла определяется первыми трёмя буквами слова. открывать файл для записи каждого слова заново — очень накладно. ваши действия?
Люди, я люблю вас! Будьте бдительны!!!
Re[4]: Мастер-класс по ФП
От: dmz Россия  
Дата: 01.01.09 15:22
Оценка:
DM>Мой вариант первой задачи на Окамле. Не слишком красиво, зато линейная сложность (никаких сортировок).

Если писать деструктивно, то можно так (чуть меньше кода):
open Std
open ExtList.List
open ExtString
open ExtHashtbl


let group l = 
    let hsh =  Hashtbl.create 1000 in
    let k s = if String.length s > 0 && ((s.[0] >= 'A' && s.[0] <= 'Z') || (s.[0] >= 'a' && s.[0] <= 'z'))
              then String.sub s 0 1 else "" in
    let step x = let key = k x in
                 if not (Hashtbl.exists hsh key) then Hashtbl.add hsh key (x::[])
                 else let item = Hashtbl.find hsh key in Hashtbl.replace hsh key (x::item)
    in Enum.iter step l ; hsh

let _ = 
    let hsh = group (input_lines stdin) in
    Enum.iter ( fun x -> if x <> "" then output_file x (String.join "\n" (Hashtbl.find hsh x) )) (Hashtbl.keys hsh)
Re[4]: Мастер-класс по ФП
От: dmz Россия  
Дата: 01.01.09 15:30
Оценка:
dmz>>Ну так можно привести тривиальное сишное решение хотя бы первой задачи?

BB>Вариант №1. Do not optimize prematurely...

BB>Вариант №2.

Ща тут будет дуэль на мясорубках, так что давайте по-честному.
1) Файлы проверять на то, что бы имя было из букв (сказано же, что из букв?)
2) Читать таки давайте из консоли, а то смысл зашивать список в программе?
3) Писать весь код — что бы можно было скомпилить и запустить — инклуды, main
4) Память за собой чистить
Re[5]: Мастер-класс по ФП
От: BulatZiganshin  
Дата: 01.01.09 15:49
Оценка:
Здравствуйте, dmz, Вы писали:

dmz>Ща тут будет дуэль на мясорубках, так что давайте по-честному.


оно кому нужно??? задача стоит в сдвиге парадигмы мышления, и насильно мил не будешь
Люди, я люблю вас! Будьте бдительны!!!
Re[5]: Мастер-класс по ФП
От: dmz Россия  
Дата: 01.01.09 15:57
Оценка:
BZ>отлично. а теперь условия задачи меняются — имя файла определяется первыми трёмя буквами слова. открывать файл для записи каждого слова заново — очень накладно. ваши действия?

Ну я и так повторно файл не открываю (хотя может надо — тогда отыграем строчек у ++ )

open Std
open ExtList.List
open ExtString

(* group -  нигде нет. тормозно, но можно переписать на хэшах - будет быстро. *)
let rec group f l = match l with
     | x::xs -> let m,n = partition (fun y -> (f x) = (f y)) xs in [x :: m] @ group f n
     | []    -> []

let dump_words = 
    let good c = match c with 
        | 'A' .. 'Z' -> c
        | 'a' .. 'z' -> c
        | _ -> '_' in
    let sanitize x = String.map good x in
    let fname x  = sanitize (if String.length x >= 3 then String.sub x 0 3 else x) in
    let read     = input_list stdin in
    let filt l   = filter (fun s -> s <> "") l in
    let dump = iter ( fun x -> output_file (fname (hd x)) (String.join "\n" (x)) ) 
    in dump (group fname (filt read))

let _ = dump_words
Re[6]: Мастер-класс по ФП
От: dmz Россия  
Дата: 01.01.09 15:59
Оценка:
BZ>оно кому нужно??? задача стоит в сдвиге парадигмы мышления, и насильно мил не будешь

Так весело же. И потом — ФП же должно уменьшать количество писанины или нет? А так же тестирования и отладки.
Re[4]: Мастер-класс по ФП
От: dmz Россия  
Дата: 01.01.09 16:04
Оценка:
BB>На плюсах.
BB>Вариант №1. Do not optimize prematurely...

#include <vector>
#include <iostream>

using namespace std;

int main(int,char**)
{
    vector<string> words;

    for(int i = 0, end = words.size(); i != end; ++i)
    {
       ofstream(words[i].substr(0,1).c_str(), ios::app) << words[i] << ' ';
    }

}


Все-таки, допишите сюда, пожалуйста, кто-нибудь, чтение вордлиста из stdin — мне просто интересно, во что
выльется повторное открытие файла. И что-то надо сделать, что бы компилятор не ругался:

dmz@x200:~/prj/ocaml/shit$ g++ ./t10cc.cc 
./t10cc.cc: In function ‘int main(int, char**)’:
./t10cc.cc:12: error: invalid use of incomplete type ‘struct std::ofstream’
/usr/include/c++/4.3/iosfwd:89: error: declaration of ‘struct std::ofstream’
Re[4]: Мастер-класс по ФП
От: dmz Россия  
Дата: 01.01.09 16:22
Оценка:
BB>for(int i = 0, end = words.size(); i != end; ++i)
BB>{
BB> ofstream(words[i].substr(0,1).c_str(), ios::app) << words[i] << ' ';
BB>}
BB>[/ccode]

Что будет, если в списке файлов попадется ' ' ? . ? .. ? CON ? PRN ?

BB>[ccode]


BB>ofstream* ofs[26];

...
BB> ofs[c — 'a'] = new ofstream(string(1, c).c_str());

Деструкторы не сработают — файлы не закроются? В приведенном решении на окамле
все ужимки с группировкой были для того, что бы избежать открытий и закрытий файлов —
файл пишется одной атомарной функцией. Если файлы открывать, и не заботиться о
закрытии — то писанины (на окамле) сильно убавится — например, можно будет просто писать за один
проход. Причем при выходе мусор должен подсобраться, и файлы все таки закроются. А вот в ++ при таком
выделении — закроются?
Re[5]: Мастер-класс по ФП
От: Basil B Россия  
Дата: 01.01.09 16:26
Оценка:
Здравствуйте, dmz, Вы писали:

dmz>Ща тут будет дуэль на мясорубках, так что давайте по-честному.


Я пас.
Re[6]: Мастер-класс по ФП
От: dmz Россия  
Дата: 01.01.09 16:30
Оценка:
dmz>>Ща тут будет дуэль на мясорубках, так что давайте по-честному.

BB>Я пас.


Тогда фиксируем результат, что если привести с++ — ный код в рабочее состояние — то это будет уже не две строчки, а как
минимум не меньше, чем в ФП-подходе? А вот если начать налагать доп. условия, типа имя файла -> первые три буквы,
или, например, попросить писать в каждый файл только уникальные строки — ++ начинает сливать?
Re[5]: Мастер-класс по ФП
От: Аноним  
Дата: 01.01.09 16:30
Оценка:
Здравствуйте, BulatZiganshin, Вы писали:

BZ>отлично. а теперь условия задачи меняются — имя файла определяется первыми трёмя буквами слова. открывать файл для записи каждого слова заново — очень накладно. ваши действия?


Если в лоб решать, то:

vector<string> words;

sort(words.begin(), words.end(), Less3);

vector<string>::iterator upper, cur = words.begin(), end = words.end();

while(cur != end)
{
    upper = upper_bound(cur, end, *cur, Less3);
    
    ofstream ofs(cur->substr(0,3).c_str());

    while(cur != upper)
    {
        ofs << *cur++ << ' ';
    }
}


А Less3 определим так:
bool Less3(std::string const& l, std::string const& r)
{
  return l.substr(0,3) < r.substr(0, 3);
}
Re[6]: Мастер-класс по ФП
От: Гест Украина https://zverok.github.io
Дата: 01.01.09 16:57
Оценка:
Здравствуйте, Гест, Вы писали:

BZ>>вот и напишите этот map


Г>Ой нет. Эт я даже не представляю, с чего начинать. В смысле, если писать на Рубях, нужна видимо какая-то библиотека для параллельности, а я такими вопросами не задавался


Я был нетрезв.
module Enumerable
    def parallel_map(&block)
        results = []
        threads = self.to_enum(:each_with_index).
            map{|obj, i| Thread.new{results[i] = block.call(obj)} }.each{|t| t.join}
        
        results
    end
end

#тестируем
r = [1,2,3,4,5].parallel_map{|i| p Thread.current; i+5}
p r

выводит:
#<Thread:0x297db6c run>
#<Thread:0x297da18 run>
#<Thread:0x297d900 run>
#<Thread:0x297d7e8 run>
#<Thread:0x297d6d0 run>
[6, 7, 8, 9, 10]


Но че-то мне это не кажется особо "функциональным"
К тому же (почему я сходу не сообразил, что требуется) в руби потоки зеленые. Так что на самом деле все эти усилия никакого выигрыша на многопроцессороной машине не дадут

Впрочем, в Ruby 1.9 они уже нативные.

ЗЫ: про ошибку в задаче с папками уже понял, как ее исправить — тоже очевидно.
Re[5]: Мастер-класс по ФП
От: dmz Россия  
Дата: 01.01.09 17:30
Оценка:
BZ>Здравствуйте, Гест, Вы писали:

Г>>А, понял. Я подумал про промежуточную структуру, но решил что это выходит за рамки задачи.


BZ>ну понятно же, что меньше O(N) на самом списке не получишь


Это еще вопрос, как получить O(N) для построения словаря dir1 -> [sudir1, ...] именно из списка вида
/dir1/subdir1/subsubdir1
/dir1/subdir2/subsubdir2
...


(а не с самой файловой системы или еще откуда)

ведь нам для каждого каталога уровня N надо перечислить все каталоги уровня N+1.
получать-то потом можно за константу, не в том вопрос. Это точно решается в таком виде именно за O(N) ?

Тупо в лоб как-то так:
open Std
open ExtList.List
open ExtString

let delim = "/"

let spath s = filter (fun x -> x <> "") (String.nsplit s delim)

let rec group f l = match l with
     | x::xs -> let m,n = partition (fun y -> (f x) = (f y)) xs in [x :: m] @ group f n
     | []    -> []

let gen_p p =
    let path = spath p in
    let ml = length path 
    in let rec ggen l path  = 
            if l < ml then (String.join delim (take l path), nth path (l)) :: ggen (l+1) path
            else (String.join delim (take l path), "")::[]
    in ggen 1 path


let gen_all l = map ( fun x -> (fst (hd x), map snd x )) (group fst (fold_left (@) [] (map gen_p l)))

(* можно хэш построить,  но лень -  суть понятна *)
let _ = List.accos "home" gen_all (input_list stdin)
Re[7]: Мастер-класс по ФП
От: D. Mon Великобритания http://thedeemon.livejournal.com
Дата: 01.01.09 17:57
Оценка:
Здравствуйте, dmz, Вы писали:

dmz> А вот если начать налагать доп. условия, типа имя файла -> первые три буквы,

dmz>или, например, попросить писать в каждый файл только уникальные строки — ++ начинает сливать?

Давайте не будем превращать тред в очередное меряние языками. Исходный замысел Булата был совсем другим, и он намного интереснее.
Re[8]: Мастер-класс по ФП
От: dmz Россия  
Дата: 01.01.09 18:00
Оценка:
DM>Давайте не будем превращать тред в очередное меряние языками. Исходный замысел Булата был совсем другим, и он намного интереснее.

Он про сдвигание парадигмы. Но парадигму надо двигать ради чего-то. Лично у меня она подвинулась, когда некоторые сложные задачи просто перестали делаться, при пересмотре подхода в сторону фп, рекурсии и отсутствия мутабельных данных — решились быстро и просто. Но это в топике не покажешь — и вообще это сложно-показуемая вещь. Всегда ведь можно упереться и сделать на чем угодно.
Re[5]: Мастер-класс по ФП
От: D. Mon Великобритания http://thedeemon.livejournal.com
Дата: 01.01.09 18:01
Оценка:
Здравствуйте, dmz, Вы писали:

dmz>Если писать деструктивно, то можно так (чуть меньше кода):


Кстати, этот вариант можно еще упростить, если вспомнить, что Hashtbl.add сам добавляет значение в список, а Hashtbl.find_all выдает весь список.
Re[6]: Мастер-класс по ФП
От: dmz Россия  
Дата: 01.01.09 18:05
Оценка:
DM>Кстати, этот вариант можно еще упростить, если вспомнить, что Hashtbl.add сам добавляет значение в список, а Hashtbl.find_all выдает весь список.

Что-то он мне вообще не нравится. Надо написать на чем-то (пусть грязном) group, куда-то его запрятать, и дальше писать чисто. Кстати, хэштейбл идет ноздря в ноздрю с решением на массивах, даже иногда быстрее (правда в пределах погрешности). Так что стараться сделать максимально грязно не стоит — достаточно написать group с изменяемым состоянием и все.
Re[5]: Мастер-класс по ФП
От: Basil B Россия  
Дата: 01.01.09 19:45
Оценка: 19 (3)
Здравствуйте, BulatZiganshin, Вы писали:

BZ>отлично. а теперь условия задачи меняются — имя файла определяется первыми трёмя буквами слова. открывать файл для записи каждого слова заново — очень накладно. ваши действия?


Спешл фор dmz компилирующийся код.

#include <string>
#include <fstream>
#include <map>

typedef std::map<std::string, std::string> StringToString;

int main(int /*argc*/, char* /*argv[]*/)
{
    using namespace std;

    StringToString m;

    ifstream in("corncob_lowercase.txt"); // http://www.mieliestronk.com/corncob_lowercase.zip

    string temp;    

    while(in >> temp)
        m[temp.substr(0,3)].append(temp).append("\n");

    for(StringToString::iterator i = m.begin(), end = m.end(); i != end; ++i)
    {
        ofstream out(i->first.c_str());
        out << i->second;
    }

    return 0;
}
Re: Мастер-класс по ФП
От: yumi  
Дата: 02.01.09 06:31
Оценка: +3 -1
Здравствуйте, BulatZiganshin, Вы писали:

BZ>итак, попробуйте решить эти задачи:

BZ>1) есть список слов. надо записать в файл "a" все слова, начинающиеся на букву 'a', в файл "b" — слова на 'b' и т.д. вплоть до z
BZ>2) имеется список имён файлов в архиве:

BZ>dir1\file1

BZ>dir1\dir2\file2
BZ>dir1\dir3\file3
BZ>dir4\file4

BZ>требуется написать функцию, которая по имени каталога возвращает список файлов и подкаталогов в нём, к примеру


BZ>f "dir1" = (["file1"], ["dir2","dir3"])


BZ>a) сложностью O(кол-во файлов)

BZ>б) более эффективную

Отличные задачки, для девятиклассников. Если хочется реально продемонстрировать мастер-класс, возьми какую-нибудь более реальную задачу, результатом которой будет законченный проект. Понятное дело не слишком крупный, но и не слишком простой. Ну там, начни с архитектуры приложения, покажи как делается функциональная декомпозиция (проектирование), сравни с тем, как это выглядело бы в ООП. Где там реальный выигрыш, в чем он проявляется. Дальше реализация, тоже с указаниями и сравнениями с ООП подходом.

А вот какую задачку взять, это вопрос...
Lisp is not dead. It’s just the URL that has changed:
http://clojure.org
Re[2]: Мастер-класс по ФП
От: dmz Россия  
Дата: 02.01.09 07:34
Оценка:
Y>Отличные задачки, для девятиклассников.

Нормальные задачи. Похожи на куски задач, которые регулярно возникают.

Y>Если хочется реально продемонстрировать мастер-класс, возьми какую-нибудь более реальную задачу,

Y>результатом которой будет законченный проект. Понятное дело не слишком крупный, но и не слишком простой.

Ну вот не вопрос. Компилятор из луа-подобного языка в байткод для условной стековой VM. Или вот — взять файлы с SQL описанием таблиц
(с констрейнтами), распарсить, сгенерировать код для доступа к базе, а так же валидаторы форм (на основе констрейнтов).

Небольшие проекты, совсем. Первый — всего сотен пять строк. Никто не хочет порешать?
Булат дал неплохие примеры, которые можно не напрягаясь поделать, по фану. Что-то сильно более сложное — никто делать не будет.

Y>Где там реальный выигрыш, в чем он проявляется. Дальше реализация, тоже с указаниями и сравнениями с ООП подходом.


Сравнение ФП vs ОО смысла не имеет, т.к. программа в функциональном стиле может также являться в каком-то смысле ОО.
Сравнивать имеет смысл императивный vs декларативный подход.

Y>А вот какую задачку взять, это вопрос...


Можно взять генератор кодов хаффмана. Прочитать байты из STDIN, построить словарь, сдампить словарь + упакованные данные в STDOUT.
Re[3]: Мастер-класс по ФП
От: dmz Россия  
Дата: 02.01.09 08:45
Оценка:
dmz>Можно взять генератор кодов хаффмана. Прочитать байты из STDIN, построить словарь, сдампить словарь + упакованные данные в STDOUT.

На питоне генератор кодов пишется строки в три, что ли.
Re[4]: Мастер-класс по ФП
От: D. Mon Великобритания http://thedeemon.livejournal.com
Дата: 02.01.09 08:56
Оценка:
Здравствуйте, dmz, Вы писали:

dmz>На питоне генератор кодов пишется строки в три, что ли.


А можно посмотреть?
Re[5]: RAII in higher-order languages (Мастер-класс по ФП)
От: z00n  
Дата: 02.01.09 09:10
Оценка:
dmz>Деструкторы не сработают — файлы не закроются? В приведенном решении на окамле
dmz>все ужимки с группировкой были для того, что бы избежать открытий и закрытий файлов —
dmz>файл пишется одной атомарной функцией. Если файлы открывать, и не заботиться о
dmz>закрытии — то писанины (на окамле) сильно убавится — например, можно будет просто писать за один
dmz>проход.

Чтобы файлы закрывались (в том числе при исключениях) есть RAII (так в С++ называют scoped resource management) — причем в окамле его сделать даже легче, чем в С++, поскольку есть ФВП.

how to do RAII in higher-order languages
Пример на Scala

Вот пример на на диалекте луа:
-- (* RAII facilities *)
fun unwind_protect(thunk, cleanup) ->
  local ok, result = pcall(thunk)
  if cleanup then cleanup() end
  if not ok then error(result,0) else return res end
end

fun with_open_file(path,mode) ->
  fun (body) ->
    local file = assert(io.open(path,mode))
    return unwind_protect(fun()-> body(file) end, =>file:close())
  end
end

fun with_file_list() ->
  fun (body) ->
    local flist = {}
    return unwind_protect(
      fun()-> body(flist) end,
      fun()-> 
        for _,file in pairs(flist) do file:close() end 
      end)
  end
end


Тогда задание 1 можно записать вот так:

-- (* '$' работает как в Haskell *)
-- (* '=>' делает thunk из выражения справа: '=> expr' тоже самое, что: 'fun() -> expr end' *)
print$pcall$=>
with_open_file("words.txt","rb")$fun (infile) ->
  local words = infile:read("*a")
  return with_file_list()$fun (filelist) ->
    for word, first in words:gmatch"((%a)%a*)" do
      filelist[first] or= assert(io.open(first..".txt","wb"))
      filelist[first]:write(word,'\n')
    end
  end
end


bash$ hluago wordlist.hlua
true    nil
bash$ tail -n 3 j.txt
juxtaposition
juxtapositional
juxtapositions
Re[5]: Мастер-класс по ФП
От: dmz Россия  
Дата: 02.01.09 09:12
Оценка:
Здравствуйте, D. Mon, Вы писали:

DM>Здравствуйте, dmz, Вы писали:


dmz>>На питоне генератор кодов пишется строки в три, что ли.


DM>А можно посмотреть?


Память подвела, поболе получается (отсюда):

import heapq

def makeHuffTree(symbolTupleList):
    trees = list(symbolTupleList)
    heapq.heapify(trees)
    while len(trees) > 1:
        childR, childL = heapq.heappop(trees), heapq.heappop(trees)
        parent = (childL[0] + childR[0], childL, childR)
        heapq.heappush(trees, parent)
    return trees[0]


и это ему надо еще таблицу вероятностей символов подсунуть.
Re[6]: RAII in higher-order languages (Мастер-класс по ФП)
От: dmz Россия  
Дата: 02.01.09 09:17
Оценка:
Z>Чтобы файлы закрывались (в том числе при исключениях) есть RAII (так в С++ называют scoped resource management)

не вопрос, только в конструкции вида:


ofstream *handles[26]
...
for( ... ) 
{
   handles[i] = new ofstream(...);
}
...


предложенной в топике, хэнлды не закроются. Наверное, можно было впихнуть какие-нибудь auto_ptr-ы вместо голых указателей — но это уже не ко мне. ++ забыл напрочь и как-то не тянет вспоминать. Мне вот интересно, почему в окамле нет встроенных функций group и т.п.
Re[6]: Мастер-класс по ФП
От: BulatZiganshin  
Дата: 02.01.09 09:18
Оценка:
Здравствуйте, dmz, Вы писали:

dmz>>>На питоне генератор кодов пишется строки в три, что ли.


dmz>def makeHuffTree(symbolTupleList):


при всём нашем уважении к питону это построитель дерева, а не генератор кодов
Люди, я люблю вас! Будьте бдительны!!!
Re[7]: Мастер-класс по ФП
От: dmz Россия  
Дата: 02.01.09 09:26
Оценка:
dmz>>def makeHuffTree(symbolTupleList):

BZ>при всём нашем уважении к питону это построитель дерева, а не генератор кодов


Уел. Если приделать перестроение дерева, что бы удобно генерить код, то в объеме кода разницы нет с тем-же окамлом.


# module Huffman : sig
    val encode : (int * int) list -> string -> (bool -> unit) -> unit
    val decode : (int * int) list -> (unit -> bool option) -> string
  end = struct
    type node =
      | Leaf of int
      | Node of t * t
    and t = int * node
  
    let rec build roots =
      match List.sort compare roots with
      | [] -> invalid_arg "Huffman"
      | [h] -> h
      | (p1, _ as t1)::(p2, _ as t2)::t ->
          build((p1 + p2, Node(t1, t2)) :: t)
  
    let mk_tree freqs =
      build((0, Leaf(-1))::List.map (fun (p, c) -> p, Leaf c) freqs)
  
    let rec mk_table = function
      | _, Leaf c -> [c, []]
      | _, Node(t1, t2) ->
          let cons b (c, t) = c, b::t in
          List.map (cons true) (mk_table t1) @
            List.map (cons false) (mk_table t2)
  
    let encode freqs string write_bit =
      let table = mk_table(mk_tree freqs) in
      let rec emit char =
        let byte = Char.code char in
        List.iter write_bit (List.assoc byte table) in
      String.iter emit string;
      List.iter write_bit (List.assoc (-1) table)
  
    let rec decode_aux read_bit t tree buf =
      match t with
      | _, Leaf(-1) -> Buffer.contents buf
      | _, Leaf byte ->
          Buffer.add_char buf (Char.chr byte);
          decode_aux read_bit tree tree buf
      | _, Node(t1, t2) ->
          match read_bit(), t1, t2 with
          | None, _, _ -> invalid_arg "decode"
          | Some true, t, _ | Some false, _, t ->
              decode_aux read_bit t tree buf
  
    let decode freqs read_bit =
      let tree = mk_tree freqs in
      decode_aux read_bit tree tree (Buffer.create 1024)
  end;;
module Huffman :
  sig
    val encode : (int * int) list -> string -> (bool -> unit) -> unit
    val decode : (int * int) list -> (unit -> bool option) -> string
  end
Re[7]: RAII in higher-order languages (Мастер-класс по ФП)
От: D. Mon Великобритания http://thedeemon.livejournal.com
Дата: 02.01.09 11:35
Оценка: 3 (1)
Здравствуйте, dmz, Вы писали:

dmz> Мне вот интересно, почему в окамле нет встроенных функций group и т.п.


У него стандартная библиотека вообще очень бедная по сравнению с хаскеллом.
Но при желании можно подключить такую штуку:
http://github.com/kig/preludeml/tree/master/prelude.ml
хотя мне в большинстве случаев хватает ExtLib + немного отсебятины.
Re[6]: Мастер-класс по ФП
От: D. Mon Великобритания http://thedeemon.livejournal.com
Дата: 02.01.09 11:43
Оценка:
Здравствуйте, dmz, Вы писали:

dmz>def makeHuffTree(symbolTupleList):..


А, ясно. При наличии реализованной бинарной кучи на С++ и др. языках получится то же самое.
Вообще, пока что мы в этом треде видим, что короче и проще те решения, где есть подходящие готовые библиотечные функции.
Re[7]: Мастер-класс по ФП
От: dmz Россия  
Дата: 02.01.09 12:37
Оценка:
DM>А, ясно. При наличии реализованной бинарной кучи на С++ и др. языках получится то же самое.

Ну так оно в питоне в стандартную библиотеку входит — тащить ниоткуда не надо.

DM>Вообще, пока что мы в этом треде видим, что короче и проще те решения, где есть подходящие готовые библиотечные функции.


Ну да, в общем. ФП помогает решать проблемы, которые появляются при определенной критической массе кода, или сложности, или
необходимости модификации. Это никак в рамках топика не рассмотришь, остается только или принять на веру, или самому напороться.
Re: Мастер-класс по ФП
От: NotGonnaGetUs  
Дата: 03.01.09 13:43
Оценка:
Здравствуйте, BulatZiganshin, Вы писали:

BZ> есть список слов. надо записать в файл "a" все слова, начинающиеся на букву 'a', в файл "b" — слова на 'b' и т.д. вплоть до z


Решил вспоминть синтаксис haskell и, проигнорировав api для записи в файл, накалякал такое решение:
import System.IO
import Data.List

input = ["abc", "def", "ijk", "axxx", "der", "preved", "%%%%%", "12345"]

main = mapM_ print [(fileName, listOfWords) | fileName <- map (:[]) ['a'..'z'],  
                                              listOfWords <- [filter (isPrefixOf fileName) input], 
                                              length listOfWords > 0]


После чего глубоко задумался.

Задачка, конечно, игрушечная, но что было бы будь это "реальная" задача. Как бы я тогда её решал?

Прежде всего обобщил бы постановку задачи:

Дано: источник данных, критерий разбиения данных на группы
Надо: данные разбить на группы, обработать данные в группах.

В итоге на свет явлись бы интерфейсы и классы:
interface IGroup<T>
{
    boolean add(T data); //true - data accepted, false - not accepted.

    void done(); //no more data, group finished.
}

//обработчик произвольных данных 
interface IProcessor<T>
{
    void add(T data); 
    void done(); //no more data
}

//пишет в файл
class FileProcessor implements IProcessor<String> 
{
    FileProcessor(String fileName) { ... }
    ...
}
//пишет в консоль
class ConsoleProcessor implements IProcessor<String> 
{
    ConsoleProcessor(String prefix) { ... }
    ...
}

interface IFilter<T>
{
    boolean match(T t);
}

//фильтр по первой букве
class ByFirstLetter implements IFilter<String>
{
    ByFirstLetter(char letter) {...}
    ...
}

//фильтрует и обрабатывает данные 
class FilteredGroup<T> implements IGroup<T>
{
    private IProcessor<T> processor;
    private Filter<T> filter;

    public FilteredGroup(IProcessor<T> processor, Filter<T> filter)
    {
        this.processor = processor;
        this.filter = filter;
    }

    public boolean add(T t)
    {
        if (filter.match(t))
        {
            processor.add(t);
            return true;
        }
        return false;
    }

    public void done()
    {
        processor.done();
    }
}

class СompositeSeqGroup<T> implements IGroup<T>
{
    private ArrayList<IGroup<T>> groups;

    СompositeSeqGroup(List<IGroup<T>> groups)
    {
        this.groups = new ArrayList<IGroup<T>>(groups);
    }

    public boolean add(T t)
    {
        for (int i = 0; i < groups.size(); i++)
        {
            if (groups.get(i).add(t))
            {
                return true;
            }
        }
        return false;
    }

    public void done()
    {
        for (int i = 0; i < groups.size(); i++)
        {
            groups.get(i).done();
        }
    }
}


И само решение:
    public static void main(String[] args)
    {
        ArrayList<IGroup<String>> groups = new ArrayList<IGroup<String>>();
        for (char letter = 'a'; letter <= 'z'; letter++)
        {
            groups.add(new FilteredGroup<String>(new FileProcessor(Character.toString(letter)), //new ConsoleProcessor(Character.toString(letter)), 
                                                 new ByFirstLetter(letter)));
        }
        
        IGroup<String> group = new CompositeSeqGroup<String>(groups)
 
        //Этот код может быть вынесен в класс CompositeSeqGroup, дабы не повторяться...
        for (int i = 0; i < args.length; i++)
        {
            group.add(args[i]);
        }
        group.done();
    }


При этом в моих руках остаётся возможность менять политики обработки (как и когда сбрасывать данные в файл или слать в сокет и т.п.), можно заменить CompositeSeqGroup на AsyncCompositeGroup так, что группы не будут ждать друг-друга...
И всё это при минимальных изменениях в коде.

Пытясь же изобразить тоже самое колдовство на haskell — начинаю теряться.

Может кто-нибудь из "гуру" набросать, как будет выглядеть "обобщённое" решение 1-ой задачки в функциональном стиле?
Re[2]: Мастер-класс по ФП
От: BulatZiganshin  
Дата: 03.01.09 14:05
Оценка:
Здравствуйте, NotGonnaGetUs, Вы писали:

NGG>В итоге на свет явлись бы интерфейсы и классы:


NGG>При этом в моих руках остаётся возможность менять политики обработки (как и когда сбрасывать данные в файл или слать в сокет и т.п.), можно заменить CompositeSeqGroup на AsyncCompositeGroup так, что группы не будут ждать друг-друга...

NGG>И всё это при минимальных изменениях в коде.

мне всё это напомнило программу сложения 2+2 на 50 строк. на яве, со всеми полагающимися bells and whistles

NGG>Пытясь же изобразить тоже самое колдовство на haskell — начинаю теряться.


NGG>Может кто-нибудь из "гуру" набросать, как будет выглядеть "обобщённое" решение 1-ой задачки в функциональном стиле?


просто реализовываешь sort_and_groupOn и дальше отдаёшь ей какие тебе нужно предикаты, например

main = do readFile "words" >>== words >>== sort_and_groupOn (take 3)
>= mapM_ (\list -> writeFile (take 3 (head list)) (unwords list))

это из моей станд. библиотеки:


(>>==) = flip fmap

---------------------------------------------------------------------------------------------------
---- Операции над списками ------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------

-- |Sort and Group list by function result
sort_and_groupOn  f  =  groupOn f . sortOn  f
sort_and_groupOn' f  =  groupOn f . sortOn' f

-- |Sort list by function result (use Schwarznegian transform)
sortOn  f  =  map snd . sortOn' fst . map (keyval f)

-- |Sort list by function result (don't use Schwarznegian transform!)
sortOn' f  =  sortBy (map2cmp f)

-- |Group list by function result
groupOn f  =  groupBy (map2eq f)

-- Utility functions for list operations
keyval  f x    =  (f x, x)                -- |Return pair containing computed key and original value
map2cmp f x y  =  (f x) `compare` (f y)   -- |Converts "key_func" to "compare_func"
map2eq  f x y  =  (f x) == (f y)          -- |Converts "key_func" to "eq_func"



в первую очередь ФП — это мощные средства комбинирования функций (алгоритмов). в ООП такого нет, поэтому традиционный способ описания функторов — это создать класс с виртуальной функцией, которая будет перекрываться в различных реализациях. это неудобно и писанины много. в фп ты просто передаёшь функцию как параметр и можешь легко создавать многоуровневые решения, не боясь запутаться. таким образом, пропадает необходимость описывать монолитный класс, включающий всю необходимую тебе функциональность — порграмма становится более модульной, каждая функция решает ровно одну задачу
Люди, я люблю вас! Будьте бдительны!!!
Re[2]: Мастер-класс по ФП
От: dmz Россия  
Дата: 03.01.09 14:16
Оценка:
NGG>После чего глубоко задумался.

NGG>Задачка, конечно, игрушечная, но что было бы будь это "реальная" задача. Как бы я тогда её решал?


NGG>В итоге на свет явлись бы интерфейсы и классы:


Жжуть.

NGG>Может кто-нибудь из "гуру" набросать, как будет выглядеть "обобщённое" решение 1-ой задачки в функциональном стиле?


Я не гуру и вообще не настоящий сварщик, но. Определим функцию фильтра, которая будет фильтровать ввод,
функцию преобразования — которая будет преобразовывать содержимое файла к тому виду, который нам нужен, и функцию
группировки — соответственно, для группировки, и функцию, которая обрабатывает результаты. И как нибудь так
(на абстрактном языке, но видимо, можно и на хаскелле [а может, даже на окамле]):


input = input_list

filter = filter ( start_with "[:digit:|:letter:]" ) 

preprocess x = x

by_prefix = ...

output = (* открываем файл и пишем, как-то так: *) iter ( fun x -> output_file (mk_fname) (String.join "\n" (x)) )

do_job_with x = input x >>> filter >>> preprocess >>> group by_prefix >>> output
Re[3]: Мастер-класс по ФП
От: FR  
Дата: 03.01.09 14:48
Оценка:
Здравствуйте, dmz, Вы писали:

dmz>группировки — соответственно, для группировки, и функцию, которая обрабатывает результаты. И как нибудь так

dmz>(на абстрактном языке, но видимо, можно и на хаскелле [а может, даже на окамле]):

На Ocamle при желании можно и вариант с интерфейсами и классами повторить
Re[4]: Мастер-класс по ФП
От: dmz Россия  
Дата: 03.01.09 14:53
Оценка: :)
FR>На Ocamle при желании можно и вариант с интерфейсами и классами повторить

Причем, разобрав джавовский синтаксис и скомпилировав его в AST окамла при помощи caml4p (если, конечно, джава автоматически парсится).
Re[3]: Мастер-класс по ФП
От: NotGonnaGetUs  
Дата: 03.01.09 16:28
Оценка: -1
Здравствуйте, BulatZiganshin, Вы писали:

BZ>мне всё это напомнило программу сложения 2+2 на 50 строк. на яве, со всеми полагающимися bells and whistles


Естественно, не зря же я сделал оговорку про игрушечность Это просто иллюстрация подхода: пушкой по воробьям, как говориться.
Но пушкой и воробья и стену кирпичную пробъёшь, а в вот рагаткой стену не пробить.

Мне хочется понять как работает функциональная пушка. Пока я вижу только как мелочёвку всякую щёлкать.


NGG>>Может кто-нибудь из "гуру" набросать, как будет выглядеть "обобщённое" решение 1-ой задачки в функциональном стиле?


BZ>просто реализовываешь sort_and_groupOn и дальше отдаёшь ей какие тебе нужно предикаты, например


BZ>main = do readFile "words" >>== words >>== sort_and_groupOn (take 3)

>>= mapM_ (\list -> writeFile (take 3 (head list)) (unwords list))

Типа берётся 3-й символ, а не 1-ый и предполагается, что сортировка стабильная (порядок эквивалентых слов сохраняется)?
Сортировка это дополнительный logN, зачем?
Провероки на диапазон 'a'-'z' уже не нужны?
А как изменится решение, если вместо (take 3) использовать (contains 'a'), (contains 'b')... и т.д.? Т.е. одно и тоже слово может попадать в разные файлы? И как тогда определить имя файла?

Собственно эти вопросы и тревожат.
С одной стороны, когда решение это две строчки, его можно и заново написать, если требования слегка изменятся.
С другой сторы этот подход не маштабируется "на многострочное решение" или особо хитрые ребусы.
Отсюда и мои метания

BZ>в первую очередь ФП — это мощные средства комбинирования функций (алгоритмов). в ООП такого нет, поэтому традиционный способ описания функторов — это создать класс с виртуальной функцией, которая будет перекрываться в различных реализациях. это неудобно и писанины много. в фп ты просто передаёшь функцию как параметр и можешь легко создавать многоуровневые решения, не боясь запутаться.


Угу, в плане "покомбинировать" ФП молодец. Особенно пока можно обойсь "функтором", а не интерфейсом содержащим несколько методов и пока не появились интерфейсы содержащие одинаковые методы, но с разными контрактами...

Про "писанины много" тоже согласен. Раза в два, максимум в три больше. Правда, когда я на с# заворачивал функции высших порядков большой вложенности были проблемы с тем, какие же им имена дать. Очень уже не понятно как называть функцию, которая возвращает функцию, которая возращает функцию делающую то-то и то-то

"не боясь запутаться" — это, как мне кажется, относится только к "чистым" функциям без сайд эффектов и всяких lazy-хитростей. Иначе всё равно буду бояться

BZ>таким образом, пропадает необходимость описывать монолитный класс, включающий всю необходимую тебе функциональность — порграмма становится более модульной, каждая функция решает ровно одну задачу


Допустим, что при использовании функций как первокласных сущностей необходимость описывать монолитный класс пропадает, а разве откуда-то вообще следует необходимость описывать монолитный класс (н-р, при той же оо-декомпозиции)?


Я пока вижу как фп может облегчить мне (местами) жизнь при реализации методов классов.
А как я справлялся бы в функциональном мире с мегабайтами исходных кодов, над которыми работает хотя бы десяток человек, — не представляю, к сожалению...
Re[4]: Мастер-класс по ФП
От: FR  
Дата: 03.01.09 17:37
Оценка:
Здравствуйте, NotGonnaGetUs, Вы писали:


NGG>Угу, в плане "покомбинировать" ФП молодец. Особенно пока можно обойсь "функтором", а не интерфейсом содержащим несколько методов и пока не появились интерфейсы содержащие одинаковые методы, но с разными контрактами...


Так для нескольких функций есть модули, которые в функциональных языках нечто среднее между модулями и объектами из императивных, например в OCaml http://people.cis.ksu.edu/~ab/Courses/505/fall08/htmlman-3.07/manual004.html Кстати функтор в терминологии Ocaml'а http://people.cis.ksu.edu/~ab/Courses/505/fall08/htmlman-3.07/manual004.html#htoc15 манипулирует как раз интерфейсами содержащими несколько методов.
Re[2]: Мастер-класс по ФП
От: Tonal- Россия www.promsoft.ru
Дата: 03.01.09 17:53
Оценка:
Задача (а) как-то так:
import Data.List

lst = [
  "dir1\\file1", "dir1\\dir2\\file2", "dir1\\dir3\\file3", "dir4\\file4"]

f lst d = (files, nub [takeWhile (/='\\') x | x <- dirs])
  where
    ff [] = lst
    ff xs = [drop ld x | x <- lst, take ld x == dd]
    dd = d ++ "\\"
    ld = length dd
    (files, dirs) = partition (notElem '\\') $ ff d

Только тут сложность получается не O(N), где N — количество путей в списке, а как минимум O(N*M*К) где M — длинна запрашиваемого пути и К — общее количество файлов начиная с запрашиваемого корня.
... << RSDN@Home 1.2.0 alpha 4 rev. 0>>
Re[5]: Мастер-класс по ФП
От: NotGonnaGetUs  
Дата: 03.01.09 18:36
Оценка:
Здравствуйте, FR, Вы писали:

NGG>>Угу, в плане "покомбинировать" ФП молодец. Особенно пока можно обойсь "функтором", а не интерфейсом содержащим несколько методов и пока не появились интерфейсы содержащие одинаковые методы, но с разными контрактами...


FR>Так для нескольких функций есть модули, которые в функциональных языках нечто среднее между модулями и объектами из императивных, например в OCaml http://people.cis.ksu.edu/~ab/Courses/505/fall08/htmlman-3.07/manual004.html Кстати функтор в терминологии Ocaml'а http://people.cis.ksu.edu/~ab/Courses/505/fall08/htmlman-3.07/manual004.html#htoc15 манипулирует как раз интерфейсами содержащими несколько методов.


Т.е. это что-то из той же области что и классы в haskell? Механизмы позволяющие опеределить интерфейс для которого можно задать разные реализации.
У меня почему-то стойкое подозрение, что если пустить их в ход, то лаконичности в функциональном решении станет ощутимо меньше, зато гибкости прибудет.
Re[3]: Мастер-класс по ФП
От: BulatZiganshin  
Дата: 03.01.09 18:38
Оценка: 3 (1)
Здравствуйте, Tonal-, Вы писали:

T>Задача (а) как-то так:


как-то всё это сложно
f list dir = list
  .$ mapMaybe (stripPrefix (make dir))         -- отберём элементы внутри каталога dir
                                               --   и отрежем от них префикс dir\
  .$ partition ('\\' `notElem`)                -- разделим на файлы и каталоги
  .$ mapSnd (nub . map (takeWhile (/='\\')))   -- удалим дубликаты из списка каталогов

-- Добавить \ к имени каталога, если оно непустое
make ""  = ""
make dir = dir++"\\"

-- Функция, которая у других авторов обозначается как |>
-- В общем-то, заменяет нам вызов метода класса :)
a.$f = f a

-- Ещё одна функция из моей станд. библиотеки
mapSnd f (a,b) = (a, f b)


T>Только тут сложность получается не O(N), где N — количество путей в списке, а как минимум O(N*M*К) где M — длинна запрашиваемого пути и К — общее количество файлов начиная с запрашиваемого корня.


да, где-то так
Люди, я люблю вас! Будьте бдительны!!!
Re[6]: Мастер-класс по ФП
От: FR  
Дата: 03.01.09 20:20
Оценка:
Здравствуйте, NotGonnaGetUs, Вы писали:

NGG>Т.е. это что-то из той же области что и классы в haskell? Механизмы позволяющие опеределить интерфейс для которого можно задать разные реализации.


Ну по сути да, но кажется поближе к объектам чем в Haskell

NGG>У меня почему-то стойкое подозрение, что если пустить их в ход, то лаконичности в функциональном решении станет ощутимо меньше, зато гибкости прибудет.


Угу, вот только у меня такое же стойкое подозрение, что для этой задачи (смотрю на последнее решение Булата) и для многих других никакие интерфейсы просто не нужны, и результат при этом не менее гибкий.
Re[7]: Мастер-класс по ФП
От: NotGonnaGetUs  
Дата: 03.01.09 21:32
Оценка:
Здравствуйте, FR, Вы писали:

FR>Угу, вот только у меня такое же стойкое подозрение, что для этой задачи (смотрю на последнее решение Булата) и для многих других никакие интерфейсы просто не нужны, и результат при этом не менее гибкий.


Не соглашусь с последней частью. Я привёл уже вопросы по решению Булата, как раз касательно гибкости... пока ответов нет.

По поводу первой части: я целиком и полностью согласен, что для _этой_ задачи можно обойтись без чего угодно.
Но если решается "реальная" задача (а не игрушечная), имеет смысл обобщить слегка задачу, чтобы прекрыть себе тылы на случай изменений в требованиях. Поэтому я привёл обобщённый вариант нашей игрушечной задачи и его решение в оо-стиле. Это тоже игра Смысл игры в том, чтобы показать как "тупое" ооп может привести к многословному, но достаточно гибкому решению.
Я просил показать, как такой же гибкости достичь методами родными для фя и как-нибудь их озвучить по-возможности. Мне эта тема очень интересна и я не первый раз задаю на rsdn.ru этот глупый вопрос
Re[8]: Мастер-класс по ФП
От: VoidEx  
Дата: 03.01.09 22:24
Оценка:
Здравствуйте, NotGonnaGetUs, Вы писали:

NGG>Здравствуйте, FR, Вы писали:


FR>>Угу, вот только у меня такое же стойкое подозрение, что для этой задачи (смотрю на последнее решение Булата) и для многих других никакие интерфейсы просто не нужны, и результат при этом не менее гибкий.


NGG>Не соглашусь с последней частью. Я привёл уже вопросы по решению Булата, как раз касательно гибкости... пока ответов нет.


Я тоже не гуру, но решение приведу
Повторил точь-в-точь ваше решение.
filterGroup :: [a] -> [(a -> Bool, a -> b)] -> [([a], a -> b)]
filterGroup _ [] = []
filterGroup d (x:xs) = (fst part, snd x) : (filterGroup (snd part) xs) where
    part = partition (fst x) d

stdOutput :: (Show a) => String -> a -> IO ()
stdOutput s x = do
    putStr $ s ++ ": "
    print x

processGroups :: [a] -> [(a -> Bool, a -> b)] -> [[b]]
processGroups d f = map (\x -> map (snd x) (fst x)) $ filterGroup d f
processGroupsIO d f = mapM_ sequence_ $ processGroups d f

filters :: [(String -> Bool, String -> IO ())]
filters = [(('a' `elem`), stdOutput "a"), (('a' `notElem`), stdOutput "not a"), (null, stdOutput "empty")]
theData = ["ala", "dfkja", "jrgh", "", "sejhr", "drt"]

main = let
    byFirstLetter c [] = False
    byFirstLetter c (x:xs) = x == c
    makeFilter :: Char -> (String -> Bool, String -> IO ())
    makeFilter c = (byFirstLetter c, stdOutput [c])
    groups = map makeFilter ['a'..'z']
    in
        processGroupsIO theData groups


В ФП ещё и проще получается. Нету лишнего IFilter, так как IGroup всё равно принимает a, а возаращает Bool, т.е. сама является фильтром. Почему у вас FilteredGroup принимает IProcessor тоже неясно. Группировать фильтры в ФП вообще плёвое дело, просто композиция функций.
Re[8]: Мастер-класс по ФП
От: VoidEx  
Дата: 03.01.09 23:03
Оценка:
Здравствуйте, NotGonnaGetUs, Вы писали:

NGG>По поводу первой части: я целиком и полностью согласен, что для _этой_ задачи можно обойтись без чего угодно.

NGG>Но если решается "реальная" задача (а не игрушечная), имеет смысл обобщить слегка задачу, чтобы прекрыть себе тылы на случай изменений в требованиях. Поэтому я привёл обобщённый вариант нашей игрушечной задачи и его решение в оо-стиле. Это тоже игра Смысл игры в том, чтобы показать как "тупое" ооп может привести к многословному, но достаточно гибкому решению.
NGG>Я просил показать, как такой же гибкости достичь методами родными для фя и как-нибудь их озвучить по-возможности. Мне эта тема очень интересна и я не первый раз задаю на rsdn.ru этот глупый вопрос

Кстати, не соглашусь с реальностью задачи. Дело не в "реальности", а в сложности и потенциальном усложнении при небольшом изменении. Пиши я в ООП, я бы тоже подумал о том, как можно комбинировать фильтры, группы, написал бы кучу интерфейсов, потому что понимал бы, что если внести одно дополнительное требование, то из 50 строк придётся выкинуть 20 и ещё 50 написать, и не дай бог эти интерфейсы используются где-то ещё, придётся и там код менять. Лучше подумать об этом заранее. Тут же было 2(3?) строки, стало 7 (при этом 2 — указание типов функций, 1 — перенос строки). Их и с нуля можно написать, так что заранее напрягаться не стоит.

Вся обобщённая задача сводится к "сгруппировать, выполнить над группами действие". Т.е. тип всей задачи: [a] -> [[a]] -> [[b]].
Первая часть достигается group, если не хватает — groupBy, если не хватает — mySuperGroup. Вторая часть — выполнение действий над группами, map.
Фактически всё, что я сделал, сделав решение таким же, как у Вас, это заменил groupBy на filterGroup и добавил дополнительные данные к группе (само действие), чтобы можно было выполнять разные действия над каждой группой.
Re[4]: Мастер-класс по ФП
От: geniepro http://geniepro.livejournal.com/
Дата: 04.01.09 05:42
Оценка:
Здравствуйте, BulatZiganshin, Вы писали:

BZ>-- Функция, которая у других авторов обозначается как |>

BZ>-- В общем-то, заменяет нам вызов метода класса
BZ>a.$f = f a

В F# конвейерный оператор обозначается как |>
Насчёи Окамля не знаю, не нашёл такого оператора в описании...
Re[4]: Мастер-класс по ФП
От: geniepro http://geniepro.livejournal.com/
Дата: 04.01.09 06:12
Оценка:
Здравствуйте, NotGonnaGetUs, Вы писали:

BZ>>main = do readFile "words" >>== words >>== sort_and_groupOn (take 3)

>>>= mapM_ (\list -> writeFile (take 3 (head list)) (unwords list))

NGG>Типа берётся 3-й символ, а не 1-ый и предполагается, что сортировка стабильная (порядок эквивалентых слов сохраняется)?


take 3 xs берёт три первых элемента списка xs, так что сортировка там по первым трём буквам...
Re[9]: Мастер-класс по ФП
От: NotGonnaGetUs  
Дата: 04.01.09 09:26
Оценка:
Здравствуйте, VoidEx, Вы писали:

VE>В ФП ещё и проще получается. Нету лишнего IFilter, так как IGroup всё равно принимает a, а возаращает Bool, т.е. сама является фильтром. Почему у вас FilteredGroup принимает IProcessor тоже неясно. Группировать фильтры в ФП вообще плёвое дело, просто композиция функций.

Обрати внимание, что у IGroup есть метод done(), а у IFilter — нет и у IProcessor метод add возвращает boolean. Эти интерфейсы реализуют разное поведение, поэтому не могут иметь общего предка (ибо LSP-in-action), даже если бы их сигнатуры были один к одному.

VE>Повторил точь-в-точь ваше решение.

Не совсем или совсем не.
Давай посмотрим:
-- твой же код, но слегка причёсанный (к вопросу о том, что практики принятые в грустном императивном мире полезны не только в нём) 
type IFilter a = a -> Bool
type IProcessor a b = a -> b
type FilteredGroup a b = (IFilter a, IProcessor a b)
 
applyFilteredGroups :: [a] -> [FilteredGroup a b] -> [([a], IProcessor a b)]
applyFilteredGroups _ [] = []
applyFilteredGroups items ((gFilter, gProcessor):groups) = 
   (matchedItems, gProcessor) : (applyFilteredGroups unmatchedItems groups) 
    where
        (matchedItems, unmatchedItems) = partition gFilter items

processGroups :: [a] -> [FilteredGroup a b] -> [[b]]
processGroups d f = map (\x -> map (snd x) (fst x)) $ applyFilteredGroups d f

processGroupsIO d f = mapM_ sequence_ $ processGroups d f

makeStdOutput :: (Show a) => String -> IProcessor a (IO())
makeStdOutput s x = do
    putStr $ s ++ ": "
    print x

makeByFirstLetter :: Char -> IFilter String
makeByFirstLetter c = isPrefixOf [c]

makeGroupFromChar :: Char -> FilteredGroup String (IO())
makeGroupFromChar c = (makeByFirstLetter c, makeStdOutput [c])

main = processGroupsIO theData $ map makeGroupFromChar ['a'..'z']


Во-первых отсутствует сущность IGroup, т.е. без нарушения OCP нельзя добавить группу не укладывающуюся в рамки FilteredGroup.
Во-вторых IProcessor имеет интерфейс a -> b, а не [a] -> b. Может это и не очень страшно, но чтобы заставить stdOutput печатать в формате 'a -> "abc", "axxx"', а не 'a -> "abc", a -> "axxx"' придётся попрыгать на ровном месте.
В-третьих, поведение FilteredGroup "вшито" в методы processGroups и applyFiltreadGroups — а как быть если нужно слова как-то (в каждой группе по своему) обработать, прежде чем отдавать в процессор или разрешить появление слова в нескольких группах? Создвать методы processGroups1,2,3 с одинаковой сигнатурой (так и до массового копипаста не далеко) или один с кучей параметров (отвёртка на все случаи жизни)? CompositeGroup решал эти проблемы.

В ФП стиле очень легко и красиво решаются задачи с раз и навсегда заданными условиями. Более того, легкость решения позволяет забивать на то, что условия могут слегка меняься — переписать не проблема (вопрос только в масштабируемости такого подхода).
А если к написанию программы подходить с тех же позиций как это делается в оо мире (т.е. обобщить задачу настолько, насколько можно, но не больше — чтобы решение оставалось открытым по отношению к будущим модификациям, но не пыталось все их реализовать сей момент), то разница в количестве строчек кода и его простоты становится уже так уж и велика, а все плюсы скатываются на уровень реализации методов класса (более лаконичная и читаемая форма записи вычислительных алгоритмов) + большее количество иммутабельных структур данных и, как следствие, методов без сайд эффектов (хотя этими же бенефитами (сайд эффекты, иммутабельность) никто не запрещает воспользоваться в императивной программе (это рекомендует делать sun, это есть в скале/немерле, даже idea (java ide) содержит возможность (на радость мне) подсвечивать переменные, которым присваиваются разные значения в ходе жизни метода как ошибочные).
Т.е. плюсы в ФП есть, но сказать, что они кординально изменяют дисциплину программирования, как-то не получается...
Re[10]: Мастер-класс по ФП
От: dmz Россия  
Дата: 04.01.09 10:08
Оценка: +1
NGG>Во-первых отсутствует сущность IGroup, т.е. без нарушения OCP нельзя добавить группу не укладывающуюся в рамки FilteredGroup.

По моему, вы пытаетесь думать в терминах ОО дизайна, что просто неприменимо к ФП.

process (inp filt group_by consume) = inp >>> filter filt >>> group group_by >>> consume


В этом конвейре можно настроить все — и поведение, и данные. И даже сделать так, что бы конкретное поведение определялось структурой данных на входе.

NGG>В ФП стиле очень легко и красиво решаются задачи с раз и навсегда заданными условиями.


Что? Это просто явная неправда. И непонятно, откуда это взялось.

NGG>Более того, легкость решения позволяет забивать на то, что условия могут слегка меняься — переписать не проблема (вопрос только в NGG>масштабируемости такого подхода).


Привести пример, когда ОО-дизайн масштабируется, а ФП — нет. Пока видно, что вы воспринимаете только решение задачи в знакомых терминах, а то, что в незнакомых — просто игнорируете.

NGG>А если к написанию программы подходить с тех же позиций как это делается в оо мире (т.е. обобщить задачу настолько, насколько можно, но не больше — чтобы решение оставалось открытым по отношению к будущим модификациям,


Пример закрытости ФП кода к будущим модификациям, хотя бы на примерах, приведенных выше.

NGG>Т.е. плюсы в ФП есть, но сказать, что они кординально изменяют дисциплину программирования, как-то не получается...


Они совершенно точно требуют другого подхода к дизайну. Читать, например, здесь: Problem K — хороший кейс. Там же по ссылкам или в комментариях можно найти и дизайн на C++ и еще каких-то императивных.

В данном топике задачи слишком простые, что бы к ним серьезно применять термин "дизайн". Problem K — уже гораздо интереснее.
Re[10]: Мастер-класс по ФП
От: FR  
Дата: 04.01.09 10:25
Оценка: +1
Здравствуйте, NotGonnaGetUs, Вы писали:

NGG>Т.е. плюсы в ФП есть, но сказать, что они кординально изменяют дисциплину программирования, как-то не получается...


Извини, но мне кажется у тебя все признаки синдрома "настоящего программиста, который может программировать на любом языке как на ФОРТРАНе", у меня кстати по отношению к ФП тоже присутствует, но гораздо слабее чем у тебя.
Re[11]: Мастер-класс по ФП
От: NotGonnaGetUs  
Дата: 04.01.09 11:20
Оценка:
Здравствуйте, dmz, Вы писали:


NGG>>Во-первых отсутствует сущность IGroup, т.е. без нарушения OCP нельзя добавить группу не укладывающуюся в рамки FilteredGroup.


dmz>По моему, вы пытаетесь думать в терминах ОО дизайна, что просто неприменимо к ФП.


Не совсем или совсем не

Неважно о чём идёт речь о ОО-дизайне, Ф-дизайне или каком-нибудь ещё — "сложность программирования в себе" (вольная трактовка Брукса) заключена в борьбе со сложностью и никуда не исчезает при смене "парадигмы". Способы борьбы со сложностью могут различаться по форме, но по содержанию они не изменны в любой программе. Да, я использую терминологию из ОО-мира, т.к. она широко распространнена и на эти термины можно ссылаться без необходимости тут же раскрывать их значение.
Взять теже шаблоны grasp от лармана. Они остаются актуальны при любом подходе к написанию программ (в отличии от шаблонов GoF, решающих главным образом, проблемы статической типизации).


dmz>
dmz>process (inp filt group_by consume) = inp >>> filter filt >>> group group_by >>> consume
dmz>


А если я напишу класс
сlass TaskNumberOne {
void process(Source inp, Filter filt , Group group_by, Consumer consume) {...}
}
вставив внутрь строчки для органиции потока данных по этой цепочке, я тоже разом решу все возможные проблемы?

dmz>В этом конвейре можно настроить все — и поведение, и данные. И даже сделать так, что бы конкретное поведение определялось структурой данных на входе.


Целиком и полностью согласен и нисколько с этим не спорю. Я только повторяю заезженную истину о том, что дьявол в деталях.
Как делается настройка "и поведения, и данных"? Как повторно использовать "труды" настройки?
Если расписать всё это, получатся теже яйца, что и в оо варианте вид сбоку, либо получится решение с другими характеристиками (н-р, очень короткое, но ни разу не используемое повторно, что при определённых условиях тоже может считаться хорошим решением).

NGG>В ФП стиле очень легко и красиво решаются задачи с раз и навсегда заданными условиями.

dmz>Что? Это просто явная неправда. И непонятно, откуда это взялось.
Т.е. утверждение, что "очень легко и красиво решаются" неверно?

NGG>>Более того, легкость решения позволяет забивать на то, что условия могут слегка меняься — переписать не проблема (вопрос только в масштабируемости такого подхода).


dmz>Привести пример, когда ОО-дизайн масштабируется, а ФП — нет.

dmz>Пример закрытости ФП кода к будущим модификациям, хотя бы на примерах, приведенных выше.

Гхм. Я задал вопросы как будет меняться решение Булата, потыкал в решение VoidEx — чем не примеры?

dmz>Пока видно, что вы воспринимаете только решение задачи в знакомых терминах, а то, что в незнакомых — просто игнорируете.

Как я это делаю? Какой-нибудь пример пожалуйста. Может я и впрямь чего-то не замечаю и не помимаю. Но своё не понимание я пытаюсь выразить в вопросах, ответив на которые вы могли бы его рассеять

dmz>Они совершенно точно требуют другого подхода к дизайну.

Ну, так я и прошу озвучить в чём же этот подход заключается, как при его использовании решаются проблемы борьбы со сложностью озвученные в тех же самых шаблонах грасп и т.п. Так же мне интересно, что мешает применять этот "другой" подход, скажем, лабая код на java. Станет ли он сразу не эффективным и почему? Если не станет, то в чём его плюсы перед имеющимся в наличии ООД? Если плюсы есть, то я стану использовать его в повседневной работе и буду рад безмерно.

dmz>Читать, например, здесь: Problem K — хороший кейс. Там же по ссылкам или в комментариях можно найти и дизайн на C++ и еще каких-то императивных.


Среди "каких-то императивных" есть и моё, признанное автором задачи как вполне себе ничего — http://rsdn.ru/forum/message/2740869.1.aspx
Автор: Gaperton
Дата: 23.11.07
(начало http://rsdn.ru/forum/message/2737769.1.aspx
Автор: NotGonnaGetUs
Дата: 21.11.07
).
Там же где-то и ссылка на моё решение на хаскелл...

dmz>В данном топике задачи слишком простые, что бы к ним серьезно применять термин "дизайн". Problem K — уже гораздо интереснее.

Согласен полностью. Простые. Но я же смог показать на простой задаче применение ОО-дизайна. Так же точно, мне думается, не должно быть проблемой продемонстрировать "другой подход к дизайну" на этой же задаче. Некоторые примеры были показаны, я сравнил решения, задал волнующие меня вопросы. Ответы на подобные вопросы по своему решению я могу дать, по чужим — хочу услышать.
Re[10]: Мастер-класс по ФП
От: VoidEx  
Дата: 04.01.09 11:30
Оценка:
Здравствуйте, NotGonnaGetUs, Вы писали:

NGG>Во-первых отсутствует сущность IGroup, т.е. без нарушения OCP нельзя добавить группу не укладывающуюся в рамки FilteredGroup.

Далее.
NGG>Во-вторых IProcessor имеет интерфейс a -> b, а не [a] -> b. Может это и не очень страшно, но чтобы заставить stdOutput печатать в формате 'a -> "abc", "axxx"', а не 'a -> "abc", a -> "axxx"' придётся попрыгать на ровном месте.
Не подумал. В любом случае — исправляется элементарно, убирается один map, который теперь лежит на плечах самого процессора, делая его гибче.
NGG>В-третьих, поведение FilteredGroup "вшито" в методы processGroups и applyFiltreadGroups — а как быть если нужно слова как-то (в каждой группе по своему) обработать, прежде чем отдавать в процессор или разрешить появление слова в нескольких группах? Создвать методы processGroups1,2,3 с одинаковой сигнатурой (так и до массового копипаста не далеко) или один с кучей параметров (отвёртка на все случаи жизни)? CompositeGroup решал эти проблемы.
Не понял. Как сделать функцию, которая принимает на вход процессор и функцию предобработки и возвращает процессор с предобработкой? Вроде как элементарно.

Задача стояла так — сгруппировать неким образом, выполнить над группами действия.
Выделяется группировалка, которая из списка данных делает список групп — туплов со спиком данных в группе и действием над ними.
Таких пишется, например, три штуки:
* Простая, принимающая предикат и операцию, одну на все группы
* Сложная, принимающая список пар фильтров и операций на каждую группу
* Сложная 2, как предыдущая, но элемент попадает во все группы, в которые он подходит
Что данное решение опять не предусмотрело?

Кстати, вопрос к знатокам Хаскеля, как мне из (less) :: a -> a -> Bool простым способом получить a -> a -> Ordering, дабы использовать в sortBy? Я, конечно, не развалюсь написать SortByLess, но может уже готовое есть?
Re[11]: Мастер-класс по ФП
От: BulatZiganshin  
Дата: 04.01.09 11:39
Оценка:
Здравствуйте, VoidEx, Вы писали:

VE>Кстати, вопрос к знатокам Хаскеля, как мне из (less) :: a -> a -> Bool простым способом получить a -> a -> Ordering, дабы использовать в sortBy? Я, конечно, не развалюсь написать SortByLess, но может уже готовое есть?

less2cmp (<) a b | a < b = LT
                 | b < a = GT
                 | True  = EQ


вроде не бином Ньютона
Люди, я люблю вас! Будьте бдительны!!!
Re[11]: Мастер-класс по ФП
От: NotGonnaGetUs  
Дата: 04.01.09 11:39
Оценка:
Здравствуйте, FR, Вы писали:

FR>Извини, но мне кажется у тебя все признаки синдрома "настоящего программиста, который может программировать на любом языке как на ФОРТРАНе", у меня кстати по отношению к ФП тоже присутствует, но гораздо слабее чем у тебя.


Со стороны, как говорится, виднее. Может я где-то увлёкшись и перегибаю палку, но, честное слово, не со зла

Я не пытаюсь программировать "как на фортране". Просто программирование "на фортране" привело к тому, что в голове всегда находятся вопросы относящиеся к качеству кода, формулируемые без относительно к языку на котором код пишется. Поэтому когда я слышу слова о том, что использование ФЯ приводит к качественному коду, мне, естественно, хочется услышать так же, а каким образом достигается качество, чем методы достижения качества отличаются от знакомых мне по грустному опыту программерской жизни методов.
Интерес у меня тут практический.

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

Может и в этой теме что-нибудь полезное получу для себя в итоге.
Re[12]: Мастер-класс по ФП
От: VoidEx  
Дата: 04.01.09 11:42
Оценка:
Здравствуйте, BulatZiganshin, Вы писали:

BZ>Здравствуйте, VoidEx, Вы писали:


VE>>Кстати, вопрос к знатокам Хаскеля, как мне из (less) :: a -> a -> Bool простым способом получить a -> a -> Ordering, дабы использовать в sortBy? Я, конечно, не развалюсь написать SortByLess, но может уже готовое есть?

BZ>
BZ>less2cmp (<) a b | a < b = LT
BZ>                 | b < a = GT
BZ>                 | True  = EQ
BZ>


BZ>вроде не бином Ньютона

Я потому и написал, что не разваюсь, но думал, что такая полезная функция и стандартной могла бы быть
Re[11]: Мастер-класс по ФП
От: geniepro http://geniepro.livejournal.com/
Дата: 04.01.09 11:54
Оценка:
Здравствуйте, VoidEx, Вы писали:

VE>... как мне из (less) :: a -> a -> Bool простым способом получить a -> a -> Ordering, дабы использовать в sortBy?


Если под less имеется в виду less then, то, наверное, типа такого:
lt a b = if less a b then LT else EQ
или
lt a b = if less a b then LT else GT

и вот возникает вопрос, что же предпочесть -- EQ или GT? У compare ведь три возможных значения, а у операции < -- всего два...
Re[12]: Мастер-класс по ФП
От: VoidEx  
Дата: 04.01.09 12:03
Оценка:
Здравствуйте, geniepro, Вы писали:

G>Здравствуйте, VoidEx, Вы писали:


VE>>... как мне из (less) :: a -> a -> Bool простым способом получить a -> a -> Ordering, дабы использовать в sortBy?


G>Если под less имеется в виду less then, то, наверное, типа такого:

G>
lt a b = if less a b then LT else EQ
или
lt a b = if less a b then LT else GT

G>и вот возникает вопрос, что же предпочесть -- EQ или GT? У compare ведь три возможных значения, а у операции < -- всего два...
Ну зато операцию < можно развернуть в обратную сторону, тогда можно получить все три варианта
Re[12]: Мастер-класс по ФП
От: Курилка Россия http://kirya.narod.ru/
Дата: 04.01.09 12:06
Оценка:
Здравствуйте, geniepro, Вы писали:

G>Здравствуйте, VoidEx, Вы писали:


VE>>... как мне из (less) :: a -> a -> Bool простым способом получить a -> a -> Ordering, дабы использовать в sortBy?


G>Если под less имеется в виду less then, то, наверное, типа такого:


Наверное всёж less than, хотя ошибка даж в яндексовые словари попала (сборник Бродского на самом деле называется "Less than one").
P.S. Я знаю, что зануда
Re[11]: Мастер-класс по ФП
От: NotGonnaGetUs  
Дата: 04.01.09 12:12
Оценка:
Здравствуйте, VoidEx, Вы писали:

NGG>>В-третьих, поведение FilteredGroup "вшито" в методы processGroups и applyFiltreadGroups — а как быть если нужно слова как-то (в каждой группе по своему) обработать, прежде чем отдавать в процессор или разрешить появление слова в нескольких группах? Создвать методы processGroups1,2,3 с одинаковой сигнатурой (так и до массового копипаста не далеко) или один с кучей параметров (отвёртка на все случаи жизни)? CompositeGroup решал эти проблемы.

VE>Не понял. Как сделать функцию, которая принимает на вход процессор и функцию предобработки и возвращает процессор с предобработкой? Вроде как элементарно.

Речь шла о том, что

processGroups :: [a] -> [FilteredGroup a b] -> [b]]

может обрабатывать только список из FilteredGroup. Причём делает это раз и на всегда заданным образом.

А если между этих групп нужна такая, которая забирает все слова образующие цепочку (берёт первое слово, а затем все первая буква которого, начинается с последней буквы предыдушего) и в результате обработки выплёвывает в файл с именем равным последнему слову суммарное количество слов в группе и максимальную длина цепочки набранной из подряд идущих слов?

Для данной задачи это не беда, можно быстренько переписать решение. Но не переписывать же его на каждый чих? Хочется как-то гарантировать стойкость к изменениям...

VE>Задача стояла так — сгруппировать неким образом, выполнить над группами действия.

VE>Выделяется группировалка, которая из списка данных делает список групп — туплов со спиком данных в группе и действием над ними.
VE>Таких пишется, например, три штуки:
VE> * Простая, принимающая предикат и операцию, одну на все группы
VE> * Сложная, принимающая список пар фильтров и операций на каждую группу
VE> * Сложная 2, как предыдущая, но элемент попадает во все группы, в которые он подходит
VE>Что данное решение опять не предусмотрело?

Хитрожопую группу, которую простым фильтром не получить Н-р, нужно помнить состояние, а это меняет интерфейс группировалки...

Понятно, что "хорошее" решение, к которому я не смог бы придраться существует. Интересен принцип по которым оно строилось бы с полпинка.
Re[13]: Мастер-класс по ФП
От: geniepro http://geniepro.livejournal.com/
Дата: 04.01.09 12:16
Оценка:
Здравствуйте, VoidEx, Вы писали:

VE>Ну зато операцию < можно развернуть в обратную сторону, тогда можно получить все три варианта


Да, Булат опередил :о)
cmp p a b | a `p` b   = LT
          | b `p` a   = GT
          | otherwise = EQ

xs = [ 0, 9, 1, 8, 2, 7, 3, 6, 4, 5 ]

ys = sortBy (cmp (<)) xs -- [0,1,2,3,4,5,6,7,8,9]
zs = sortBy (cmp (>)) xs -- [9,8,7,6,5,4,3,2,1,0]
Re[4]: Мастер-класс по ФП
От: BulatZiganshin  
Дата: 04.01.09 13:52
Оценка:
Здравствуйте, NotGonnaGetUs, Вы писали:

NGG>>>Может кто-нибудь из "гуру" набросать, как будет выглядеть "обобщённое" решение 1-ой задачки в функциональном стиле?


BZ>>просто реализовываешь sort_and_groupOn и дальше отдаёшь ей какие тебе нужно предикаты, например


BZ>>main = do readFile "words" >>== words >>== sort_and_groupOn (take 3)

>>>= mapM_ (\list -> writeFile (take 3 (head list)) (unwords list))

NGG>А как изменится решение, если вместо (take 3) использовать (contains 'a'), (contains 'b')... и т.д.? Т.е. одно и тоже слово может попадать в разные файлы? И как тогда определить имя файла?


написать функцию, отображающую список предикатов плюс список значений в список подсписков:

split2groups :: [a->Bool] -> [a] -> [a]]

с нею решение будет выглядеть так:
main = do readFile "words" >>== words >>== split2groups (map contains ['a'..'z']) >>== zip ["a".."z"]
            >= mapM_ (\filename list -> writeFile filename (unwords list))


NGG>Собственно эти вопросы и тревожат.

NGG>С одной стороны, когда решение это две строчки, его можно и заново написать, если требования слегка изменятся.
NGG>С другой сторы этот подход не маштабируется "на многострочное решение" или особо хитрые ребусы.

в каком смысле не масштабируется?

NGG>Угу, в плане "покомбинировать" ФП молодец. Особенно пока можно обойсь "функтором", а не интерфейсом содержащим несколько методов и пока не появились интерфейсы содержащие одинаковые методы, но с разными контрактами...


несколько методов передать можно точно так же. насчёт контрактов я не понял — дай пример

NGG>"не боясь запутаться" — это, как мне кажется, относится только к "чистым" функциям без сайд эффектов и всяких lazy-хитростей. Иначе всё равно буду бояться


я сужу по своему опыту. а ты судишь по отсутствию опыта

BZ>>таким образом, пропадает необходимость описывать монолитный класс, включающий всю необходимую тебе функциональность — порграмма становится более модульной, каждая функция решает ровно одну задачу


NGG>Допустим, что при использовании функций как первокласных сущностей необходимость описывать монолитный класс пропадает, а разве откуда-то вообще следует необходимость описывать монолитный класс (н-р, при той же оо-декомпозиции)?


а почему моё решение комбинирует несколько функций для получения нужного результата, а твоё — монолитно? я думаю, причина в том, что в C* описание класса или даже функции — достаточно многословно, поэтому есть тенденция складывать в них больше функциональности, нежели в хаскеле
Люди, я люблю вас! Будьте бдительны!!!
Re[12]: Мастер-класс по ФП
От: dmz Россия  
Дата: 04.01.09 14:28
Оценка: +1
NGG>Взять теже шаблоны grasp от лармана. Они остаются актуальны при любом подходе к написанию программ (в отличии от шаблонов GoF, решающих главным образом, проблемы статической типизации).

Боюсь мы уходим в некие обобщения, обсуждать справедливость которых я не готов.

dmz>>
dmz>>process (inp filt group_by consume) = inp >>> filter filt >>> group group_by >>> consume
dmz>>


NGG>А если я напишу класс

NGG>сlass TaskNumberOne {
NGG>void process(Source inp, Filter filt , Group group_by, Consumer consume) {...}
NGG>}

NGG>вставив внутрь строчки для органиции потока данных по этой цепочке, я тоже разом решу все возможные проблемы?


Все возможные проблемы решить вообще невозможно. Эту задачу — да, вполне можно решить и так. На джаве так обычно не делают — для нее это будет непривычный дизайн. Но вполне можно. А можно применить шаблон template method, например, а конкретные реализации параметризовать. Получится по смыслу почти то-же, что и в ФП примере.

В случае джавы контракты между inp, filter, group_by (кстати group by недостаточно, если нет group — т.к. стандартного group с известным поведением нет — а в ФП это один из базовых примитивов, тоже касается filt) определятся интерфейсами, в ФП — типами.

NGG>Как делается настройка "и поведения, и данных"? Как повторно использовать "труды" настройки?


1) передать новые реализации соответствующих методов
2) определить типы, для которых определены соответствующие реализации. Бочки фильтруем и группируем — так, а ящики — сяк.
3) определить функции, в которые уже подставлены какие-то параметры, которые скорее всего не будут меняться.

Но это все пустое — почему, см. ниже (т.к. решается не та задача)

Очевидно, что у нас есть повторно используемые компоненты — источник (input) — который генерирует последовательность сущностей, filter, группирующая функция (считает некий ключ для этих сущностей), и консьюмер, да и сам алгоритм, в общем. Который в силу полиморфизма можно использовать для других объектов, для которых определены соответствующие компоненты.

NGG>Если расписать всё это, получатся теже яйца, что и в оо варианте вид сбоку,


Да что-то вот никак не получаются.

NGG>либо получится решение с другими характеристиками (н-р, очень короткое, но ни разу не используемое повторно, что при определённых условиях тоже может считаться хорошим решением).


Да, именно. Сложное, в угоду "предположительно" повторному использованию решение простой задачи — это плохо.

NGG>Т.е. утверждение, что "очень легко и красиво решаются" неверно?


Очевидно, что неверно про раз и навсегда заданные задачи.

NGG>Гхм. Я задал вопросы как будет меняться решение Булата, потыкал в решение VoidEx — чем не примеры?


Ну сейчас ознакомлюсь более детально, хотя, конечно начинает утомлять.

dmz>>Они совершенно точно требуют другого подхода к дизайну.

NGG>Ну, так я и прошу озвучить в чём же этот подход заключается, как при его использовании решаются проблемы борьбы со сложностью озвученные в тех же

NGG>самых шаблонах грасп и т.п. Так же мне интересно, что мешает применять этот "другой" подход, скажем, лабая код на java. Станет ли он сразу не NGG>эффективным и почему? Если не станет,


Да потому, что язык не поддерживает. Честно говоря, приплетать опять грасп к этой задаче — это уже слишком. Ну вот пример — был у нас фильтр непустых строк, но, допустим, надо бы отсортировать комментарии и вообще — уметь наложить вместо одного фильтра N фильтров, но фильтры известны в компайл-тайме. Ну, в общем, вполне себе допущение, не хуже уже прочих.

Решение на ФП:


filter_a = ...
filter_b = ...
filter_c = ...

do_the_job input >>> (filter_a . filter_b . filter_c ) >>> group >>> consume

ну или 

mega_filter = filter_a . filter_b . filter_c


Решение на ОО (оппа, группового наложения мы сразу не не предусмотрели? Ну, не беда)

Решение адын:

class FilterA : Filter { десять строк }
class FilterB : Filter { десять строк }
class FilterB : Filter { десять строк }

class MegaFilter : Filter 
{
   bool apply( Entity entity ) {
      return FilterA(entity) && FilterB(entity) && FilterC(entity)
   }
};

Решение два:

class MegaFilter : Filter 
{
   MegaFilter( List<Filter>& filt ) {}

   bool apply( Entity entity ) {
      foreach( ... ) {  ... }
   }
};

Решение три - рефакторим интерфейсы


NGG>то в чём его плюсы перед имеющимся в наличии ООД? Если плюсы есть, то я стану использовать его в повседневной работе и буду рад безмерно.

NGG>Согласен полностью. Простые. Но я же смог показать на простой задаче применение ОО-дизайна.

В итоге имеем овердизайн. Задача, которую решаем решена излишне сложно, построен некий фреймворк "для решения подобных задач", при этом полнота его не вполне известна; более того, из постановки очевидно, что "принять набор сущностей, отфильтровать, сгруппировать и и отдать консьюмеру" — это не есть сама задача — это решение некоей задачи. Соответственно, бессмысленно обобщать эту задачу — надо обобщать, и делать некие предположения относительно исходной задачи. В итоге решена некая задача, необходимость решения которой неочевидна. Потрачено время. Съехали оценки времени — т.е. ставя задачу считать, отсортировать и построить индекс (если это задача построения некоего индекса) — я никак не ожидаю увидеть в итоге нечто подобное тому, что увидел. Более того, если это была именно задача построения индекса — то, например, задача передачи его по TCP о которой тут тоже задумались — не возникнет никогда ВООБЩЕ, т.е. даже 15 минут тратить на рассмотрение этого бессмысленно. Далее, если задача решается в одну-две строки — то очевидно, если ВДРУГ, когда нибудь, в следующей жизни потребуются некоторые модификации, то две строчки проще выкинуть и написать заново, если уж на то пошло. Более того, человек, который написал мега-фреймворк для сортировки ящиков, может уже не работать на момент, когда изменения понадобятся. Придет следующий человек. Получит задачу сделать так, что бы помимо ящиков, сортировались так же и бочки. Увидит фреймворк. Как думаете, он будет вникать в весь "повторно использованный код" ? Или проведет "рефакторинг" — т.е. выкинет или забьет, и напишет свое?

Ну и? Время потрачено , код bloated, больше кода — больше поддержки и документирования, больше время на въезжание новым членам команды, в общем, все при деле. Особенно хорошо становится, когда креативных людей больше одного — все читали гамму, лармана и фаулера, там столько паттернов...

Я еще раскрою тему.
Re[8]: Мастер-класс по ФП
От: BulatZiganshin  
Дата: 04.01.09 14:35
Оценка:
Здравствуйте, NotGonnaGetUs, Вы писали:

NGG>Но если решается "реальная" задача (а не игрушечная), имеет смысл обобщить слегка задачу, чтобы перекрыть себе тылы на случай изменений в требованиях. Поэтому я привёл обобщённый вариант нашей игрушечной задачи и его решение в оо-стиле. Это тоже игра Смысл игры в том, чтобы показать как "тупое" ооп может привести к многословному, но достаточно гибкому решению.


возможно, тебя не понимают потому, что обобщение в ФП — самая тривиальная операция, не требующая какой-то специальной методологии и пр. если обобщаемая часть алгоритма является синтаксически законченной, то ты просто превращаешь её в параметр:
superMain infile tokenizer detokenizer grouper = 
  readFile infile >>== tokenizer >>== sort_and_group_on grouper
    >>= mapM_ (\list -> writeFile (grouper (head list)) (detokenizer list))
Люди, я люблю вас! Будьте бдительны!!!
Re[5]: Мастер-класс по ФП
От: NotGonnaGetUs  
Дата: 04.01.09 14:52
Оценка:
Здравствуйте, BulatZiganshin, Вы писали:

BZ>
BZ>>>main = do readFile "words" >>== words >>== sort_and_groupOn (take 3) 
            >>>>= mapM_ (\list -> writeFile (take 3 (head list)) (unwords list))

BZ>main = do readFile "words" >>== words >>== split2groups (map contains ['a'..'z']) >>== zip ["a".."z"]
            >>= mapM_ (\filename list -> writeFile filename (unwords list))
BZ>

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

Угу, т.е. решение просто переписывается, как раз об этом я и писал.
Поскольку задача решалась "в одну условную строчку" это сделать просто и вопросов никаких нет.

NGG>>С другой сторы этот подход не маштабируется "на многострочное решение" или особо хитрые ребусы.

BZ>в каком смысле не масштабируется?
В том смысле, что старое решение вон и пишем новое...
А если бы задачка была не тривальная? Разве не стоит "подстелить соломку" заранее, чтобы потом было мягче падать?

BZ>несколько методов передать можно точно так же. насчёт контрактов я не понял — дай пример


Пусть у нас есть функции:
a :: Int -> Strig
b :: String->Int

a':: Int -> Strig
b':: String->Int

с:: (Int -> String) -> (String -> Int) -> String(неважно что) -> String (в неважно что)


Контракт функции с — первый и второй аргументы это прямое и обратное преоразование. Внутри функции — алгоритм которому важно выполнение этих свойств. Пары a,b и a',b' удовлетворяют этим контрактам, a,b' и a',b — нет.

Использование функции c не безопасно — н-р, можно при правке кода поменять a на a', а про b забыть...
Компилятор промолчит. Может быть даже тесты пройдут успешно, т.к. разница между b и b' крылась в каком-то редком случае почему-то не покрытому при тестировании. А в итоге приложение не отработает как следует в самый ответственный момент.

Возможно моя боязнь блуждающих по аргументам в свободном полёте связных функций это только от недостатка опыта в их готовке...
Но пока мне кажется, что разумно было бы явно декларировать зависимость между аргументами:
data Converter = Converter 
{
     encoder :: (Int->String), 
     decoder :: (String -> Int)
}

c :: Converter -> String(неважно что) -> String (в неважно что)

Из декларации сразу видно что к чему.
Разве нет?

(Конечно, приверженцы динамической типизации дружно пошлют меня в сад (пусть, не здесь с ними спорить ), но хаскелл всё-таки язык со статической типизацией.)

BZ>я сужу по своему опыту. а ты судишь по отсутствию опыта

Подколол

BZ>а почему моё решение комбинирует несколько функций для получения нужного результата, а твоё — монолитно? я думаю, причина в том, что в C* описание класса или даже функции — достаточно многословно, поэтому есть тенденция складывать в них больше функциональности, нежели в хаскеле


Гхм. Тут какое-то расхождение во взглядах на то, что такое монолитность
Мне как раз твоё решение видится монолитным кусоком кода, а моё комбинируется из нескольких классов для полученя нужного результата и каждый из этих классов может жить счастливой самостоятельной жизнью.

Само-то решение вот:
        //Создание фильтров         
        ArrayList<IGroup<String>> groups = new ArrayList<IGroup<String>>();
        for (char letter = 'a'; letter <= 'z'; letter++) //да, заменить for на map было бы приятно... но суть не меняется.
        {
            groups.add(new FilteredGroup<String>(new FileProcessor(Character.toString(letter)),
                                                 new ByFirstLetter(letter)));
        }
        
        //Задание приоритетов 
        IGroup<String> group = new CompositeSeqGroup<String>(groups);
             
        //обработка
        //место этого цикла, как я уже говорил, не правильное. он должен жить в дефолтной реализации метода IGroup#addAll()
        for (int i = 0; i < args.length; i++) 
        {
            group.add(args[i]);
        }
        group.done();

Где тут монолитность?
Re[13]: Мастер-класс по ФП
От: yumi  
Дата: 04.01.09 14:58
Оценка:
Здравствуйте, dmz, Вы писали:

dmz>Более того, человек, который написал мега-фреймворк для сортировки ящиков, может уже не работать на момент, когда изменения понадобятся. Придет следующий человек. Получит задачу сделать так, что бы помимо ящиков, сортировались так же и бочки. Увидит фреймворк. Как думаете, он будет вникать в весь "повторно использованный код" ? Или проведет "рефакторинг" — т.е. выкинет или забьет, и напишет свое?


Вы не так поняли, точнее смотрите с другой точки зрения. Только с позиции одного разработчика. Фича мега-фреймворка как раз в том, что какую-то часть, например, сортировку ящиков может делать один человек, а другой может писать сортировку бочек. Причем им нужен только интерфейс ISort.
Lisp is not dead. It’s just the URL that has changed:
http://clojure.org
Re[2]: Мастер-класс по ФП
От: dmz Россия  
Дата: 04.01.09 14:58
Оценка:
NGG>При этом в моих руках остаётся возможность менять политики обработки (как и когда сбрасывать данные в файл или слать в сокет и т.п.), можно заменить CompositeSeqGroup на AsyncCompositeGroup так, что группы не будут ждать друг-друга...

Слать это в сокет очень полезно, без сомнения. А вот сделаем реалистичное допущение относительно данной задачи в рамках ее предметной области (уж какая есть) — писать в каждый файл только уникальные строки (т.к. на кой черт писать дублирующие?)

решение на условном ФП:

(* было *)
do_job_with input filt group_by  consumer = input >>> filter filt >>> group group_by >> consumer


(* да, надо было параметризовать фильтрацию. ну ладно - рефакторим *)

do_job_with input filter group_by consumer = input >>> filter >>> group group_by >>> consumer

...

concrete_filter  = unique . filter filt 

...
Re[5]: Мастер-класс по ФП
От: D. Mon Великобритания http://thedeemon.livejournal.com
Дата: 04.01.09 15:12
Оценка:
Здравствуйте, geniepro, Вы писали:

G>В F# конвейерный оператор обозначается как |>

G>Насчёи Окамля не знаю, не нашёл такого оператора в описании...

В Окамле он не входит в язык или стандартную библиотеку, но чаще всего тоже обозначается |> и описывается отдельно: let (|>) x f = f x;;
Re[12]: Мастер-класс по ФП
От: BulatZiganshin  
Дата: 04.01.09 15:13
Оценка:
Здравствуйте, NotGonnaGetUs, Вы писали:

dmz>>
dmz>>process (inp filt group_by consume) = inp >>> filter filt >>> group group_by >>> consume
dmz>>


dmz>>В этом конвейре можно настроить все — и поведение, и данные. И даже сделать так, что бы конкретное поведение определялось структурой данных на входе.


NGG>Целиком и полностью согласен и нисколько с этим не спорю. Я только повторяю заезженную истину о том, что дьявол в деталях.

NGG>Как делается настройка "и поведения, и данных"?

передачей их в качестве параметров

NGG>Как повторно использовать "труды" настройки?


видимо, речь идёт о partial application?

NGG>А если я напишу класс

NGG>сlass TaskNumberOne {
NGG>void process(Source inp, Filter filt , Group group_by, Consumer consume) {...}
NGG>}
NGG>вставив внутрь строчки для органиции потока данных по этой цепочке, я тоже разом решу все возможные проблемы?

да, а разве нет? разница только в том, что на C# это наверно будет достаточно сложная функция

NGG>Ну, так я и прошу озвучить в чём же этот подход заключается, как при его использовании решаются проблемы борьбы со сложностью озвученные в тех же самых шаблонах грасп и т.п. Так же мне интересно, что мешает применять этот "другой" подход, скажем, лабая код на java. Станет ли он сразу не эффективным и почему? Если не станет, то в чём его плюсы перед имеющимся в наличии ООД? Если плюсы есть, то я стану использовать его в повседневной работе и буду рад безмерно.


все три C* языка активно добавляют в свой арсенал ФП фичи. поэтому давайте посмотрим скажем какие проблемы возникнут при использовании ФП подхода в чистом С или C++ 80-х годов:

1) нам нужна передача функций в качестве параметров и возвращение их в качестве результатов. это поддерживается
2) нужна поддержка анонимных лямбд. этого нет — все функции должны быть описаны глобально. но по крайней мере выразительную мощь языка это не изменяет
3) нужна поддержка частичного применения. этого нет
4) нужен двухсторонний автовывод типов с автоматическим обобщением. этого тоже нет, что затрудняет разбиение алгоритмов на множество небольших [глобальных, локальных и анонимных] функций — засребёшься описывать типы всех их параметров и результатов
5) нужно ленивое вычисление. тоже нет
6) ну и самом собой, нужен GC

в остальном ФП подходы вполне применимы и на C* языках

dmz>>В данном топике задачи слишком простые, что бы к ним серьезно применять термин "дизайн". Problem K — уже гораздо интереснее.

NGG>Согласен полностью. Простые. Но я же смог показать на простой задаче применение ОО-дизайна. Так же точно, мне думается, не должно быть проблемой продемонстрировать "другой подход к дизайну" на этой же задаче. Некоторые примеры были показаны, я сравнил решения, задал волнующие меня вопросы. Ответы на подобные вопросы по своему решению я могу дать, по чужим — хочу услышать.

моё понимание ФП дизайна — это представить себе, что ты для каждой задачи разрабатываешь свой собственный язык программирования. ты не ограничен определённым набором средств структурирования программы, типа классов или указателей. твоя задача — объяснить компьютеру как решить проблему, используя при этом любой удобный тебе алгоритмический язык. и тут на первое место выходит именно то, как сделать это объяснение наиболее читабельным и сопровождаемым

вот пример. представим классическую gui-библиотеку. реализация диалога настроек на ней может выглядеть так:
1) создаём все необходимые controls
2) заполняем их исходными данными
3) отображаем диалог
4) после нажатия Ok обновляем переменные

удобно ли это? на мой взгляд нет — нужно сгруппировать описание контрола, перевод данных в него и обратно. соответственно ФП-дизайн здесь состоит в создании высокоуровневых контролов, которые несут в себе не только свои данные, но и ассоциированные с ними действия. и тогда программа будет выглядеть так:
runDialog [
  Editbox [Color := Red, Label := "Speed", Init := (do setText =<< readVar speed), 
                                           OnOK := (do getText >>= writeVar speed)]
  SpinButton ....
]
Люди, я люблю вас! Будьте бдительны!!!
Re[14]: Мастер-класс по ФП
От: dmz Россия  
Дата: 04.01.09 15:22
Оценка: +1
Здравствуйте, yumi, Вы писали:

Y>Здравствуйте, dmz, Вы писали:


dmz>>Более того, человек, который написал мега-фреймворк для сортировки ящиков, может уже не работать на момент, когда изменения понадобятся. Придет следующий человек. Получит задачу сделать так, что бы помимо ящиков, сортировались так же и бочки. Увидит фреймворк. Как думаете, он будет вникать в весь "повторно использованный код" ? Или проведет "рефакторинг" — т.е. выкинет или забьет, и напишет свое?


Y>Вы не так поняли, точнее смотрите с другой точки зрения. Только с позиции одного разработчика. Фича мега-фреймворка как раз в том, что какую-то часть, например, сортировку ящиков может делать один человек, а другой может писать сортировку бочек. Причем им нужен только интерфейс ISort.


Тема сортировки пока никем не поднималась, но в рамках предметной области допущение, что она понадобится — вполне резонное. ISort — это, кстати, у кого будет такой интерфейс? В рамках предложенной декомпозиции? Ась?

Но смотрю я не с точки зрения разработчика. Смотрю я, допустим, с точки зрения заказчика или аутсорс-менеджера. И вижу, что вместо задачи построения дискового индекса, которую я заказывал, мне впаривают фреймворк для (цитирую)

[quote]
Дано: источник данных, критерий разбиения данных на группы
Надо: данные разбить на группы, обработать данные в группах.
[/quote]

Таких задач не бывает, но да ладно. Для увеличения вероятности того, что нам такая задача понадобится — расширим ее аудиторию с рамок данного проекта до всего мира. Выложим в сеть. Как будете искать ее решение в гугле? Ладно, допустим, мы нашли.

Что мы видим? Для того, что бы данным решением воспользоваться — нужно пользоваться контейнерным (по сути) классом Group (если у нас данные ходят в каком-нибудь обычном List — придется написать адаптер, какой — нибудь свой FromListProcessor, разобраться, чем отличаются Group от Processor с одинаковым интерфейсом (две разные сущности с внешне одинаковым поведением, тесно сцепленные), а потом еще вытащить свои данные из Group.

И это вместо того, что бы тупо в лоб воспользоваться фундаментальными примитивами group, map, filter — которые будут работать с любыми списками (не знаю, как в джаве, но в питоне есть group, filter, map прямо вот в таком виде) — на которые данная задача естественно декомпозируется.
Re[6]: Мастер-класс по ФП
От: FR  
Дата: 04.01.09 15:27
Оценка: 2 (2)
Здравствуйте, NotGonnaGetUs, Вы писали:

NGG>Гхм. Тут какое-то расхождение во взглядах на то, что такое монолитность

NGG>Мне как раз твоё решение видится монолитным кусоком кода, а моё комбинируется из нескольких классов для полученя нужного результата и каждый из этих классов может жить счастливой самостоятельной жизнью.

Так как в функциональных языках функция первоклассная сущность, твое высказывание перепишется так:

а моё комбинируется из нескольких функций для полученя нужного результата и каждая из этих функций может жить счастливой самостоятельной жизнью.


Re[12]: Мастер-класс по ФП
От: VoidEx  
Дата: 04.01.09 15:43
Оценка:
Здравствуйте, NotGonnaGetUs, Вы писали:

NGG>Хитрожопую группу, которую простым фильтром не получить Н-р, нужно помнить состояние, а это меняет интерфейс группировалки...


NGG>Понятно, что "хорошее" решение, к которому я не смог бы придраться существует. Интересен принцип по которым оно строилось бы с полпинка.


На самом деле непонятно. Потому что появляются такие усложнения, которые скорее говорят о кривом решении некой другой задачи, раз появилась такая, где для группировки скоро придётся в базу данных обращаться, причём внутри самой группировалки.
Т.е. если вот приспичило цепочку собрать, так можно её и собрать, написав элементарную функцию, а не писать обобщённую группировалку, которая бы умела всё.

type Group a b = [a] -> ([([a], [a] -> b)], [a])
passFirst g s = g s
passAll g s = (fst $ g s, s)
makeGroup :: (Eq a) => ([a] -> [a]) -> ([a] -> b) -> Group a b
makeGroup f p l = ([(m, p)], l \\ m) where m = f l
composeGroup p f n [] = ([], [])
composeGroup p f n l = (fg ++ ng, ntail) where
    (fg, ftail) = p f l
    (ng, ntail) = p n ftail
seqGroup :: (Eq a) => Group a b -> Group a b -> Group a b
seqGroup = composeGroup passFirst
unionGroup :: (Eq a) => Group a b -> Group a b -> Group a b
unionGroup = composeGroup passAll

groupAndProcess input grouping = mapM_ (\x -> (snd x) (fst x)) $ fst $ grouping input

-- Остальное ниже - семечки, к делу не относящиеся
groupCmp cmp [] = []
groupCmp cmp l@(s:_) = filter (cmp s) l
groupByWith cmp x = seqGroup (makeGroup (groupCmp cmp) x) (groupByWith cmp x)

consoleOutput prefix input = putStrLn prefix >> mapM_ (putStrLn . ("\t" ++)) input

byFirstLetter :: String -> String -> Bool
byFirstLetter _ [] = False
byFirstLetter [] _ = False
byFirstLetter (l:_) (r:_) = l == r
groupStated f i l = map fst $ filter snd $ zip l $ fst $ runState (mapM f l) i
statedFilter :: String -> State String Bool -- группировалка со стейтом
statedFilter s = do
    lastStr <- get
    if ((null lastStr) || (null s) || ((last lastStr) == head s))
        then do
            put s
            return True
        else return False
input = ["abc", "cde", "efg", "aaa", "ghi", "sxdf", "zadedf", "sfsfg", "atrhyh", "43f5", "sdcsby", "Unuufd"]

main = groupAndProcess input $ unionGroup -- union, т.е. элементы попадут в обе группы
    (makeGroup (groupStated statedFilter "") (\xs@(x:_) -> consoleOutput [head x] xs)) -- составляем цепочку слов
    (groupByWith byFirstLetter (\xs@(x:_) -> consoleOutput [head x] xs)) -- группируем по первому символу

Смотря на всё это, делаю 3 вывода:
    1. Основной код всё равно невелик, 15 строк
    2. Либо всё это пишется проще на порядок (хотелось бы тогда от гуру увидеть подтверждение)
    3. И проще решать частные задачи по 2 строки на каждую, чем делать такое обобщение, которым и пользоваться не будет никто

Собственно, то же самое, но в частном случае:
byFirstLetter :: String -> String -> Bool
byFirstLetter _ [] = False
byFirstLetter [] _ = False
byFirstLetter (l:_) (r:_) = l == r

makeChain strs = reverse $ makeChain' strs "" [] where
    makeChain' [] cur acc = acc
    makeChain' (c:cs) cur acc
        | isPrefixOf cur c = makeChain' cs [last c] (c:acc)
        | otherwise = makeChain' cs cur acc
main = consoleOutput "chain" chain >> mapM_ (\xs@(x:_) -> consoleOutput [head x] xs) groups where
    (chain, groups) = (makeChain &&& (groupBy byFirstLetter . sort)) input
Re[6]: Мастер-класс по ФП
От: BulatZiganshin  
Дата: 04.01.09 15:46
Оценка:
Здравствуйте, NotGonnaGetUs, Вы писали:

BZ>>
BZ>>>>main = do readFile "words" >>== words >>== sort_and_groupOn (take 3) 
            >>>>>= mapM_ (\list -> writeFile (take 3 (head list)) (unwords list))

BZ>>


BZ>>несколько методов передать можно точно так же. насчёт контрактов я не понял — дай пример


и чем тебя не устраивает твоё решение?

BZ>>а почему моё решение комбинирует несколько функций для получения нужного результата, а твоё — монолитно? я думаю, причина в том, что в C* описание класса или даже функции — достаточно многословно, поэтому есть тенденция складывать в них больше функциональности, нежели в хаскеле


NGG>Гхм. Тут какое-то расхождение во взглядах на то, что такое монолитность

NGG>Мне как раз твоё решение видится монолитным кусоком кода, а моё комбинируется из нескольких классов для полученя нужного результата и каждый из этих классов может жить счастливой самостоятельной жизнью.

NGG>Само-то решение вот:

NGG>Где тут монолитность?

виноват, я в твоём решении просто не разбирался. имхо, оно ничем не отличается от моего, только я ес-но использую функции, а не классы. если к примеру тебе потребуется разбивать на классы 0..9 вместо a..z, то точно также придётся менять процитированный тобой код, как и в моём решении
Люди, я люблю вас! Будьте бдительны!!!
Re[13]: Мастер-класс по ФП
От: NotGonnaGetUs  
Дата: 04.01.09 15:58
Оценка:
Здравствуйте, dmz, Вы писали:

NGG>>вставив внутрь строчки для органиции потока данных по этой цепочке, я тоже разом решу все возможные проблемы?

dmz>Все возможные проблемы решить вообще невозможно. Эту задачу — да, вполне можно решить и так.
Ну так если затем расписать все перечисленные в аргуметах интерфейсы классы, получится тоже самое количество кода что в обрисованном мной вараинте.

dmz>А можно применить шаблон template method, например, а конкретные реализации параметризовать. Получится по смыслу почти то-же, что и в ФП примере.

Так оно влюбом варианте получится тоже самое по смыслу...

dmz>Очевидно, что у нас есть повторно используемые компоненты — источник (input) — который генерирует последовательность сущностей, filter, группирующая функция (считает некий ключ для этих сущностей), и консьюмер, да и сам алгоритм, в общем. Который в силу полиморфизма можно использовать для других объектов, для которых определены соответствующие компоненты.

+
NGG>>Если расписать всё это, получатся теже яйца, что и в оо варианте вид сбоку

IFilter — filter
IGroup#add — группирующая функция (не один к одному, т.к. есть разница между группами заданными из вне и группа создаваемыми в ходе работы алгоритма (как в решении с (take 3)). "Знание" о таких группах находится в реализации CompositeGroup и там может и должен использовать дескриминатор строящий по данным группу)
IProcessor — консьюмер
СompositeGroup — сам алгоритм

Разве не яйца?

dmz>Очевидно, что неверно про раз и навсегда заданные задачи.

Наверное, очевидным должно быть, что не только для раз и на всегда заданных условий, а для более широго круга задач
Вот собственно поддтверждения этому я и хочу найти.

NGG>>Ну, так я и прошу озвучить в чём же этот подход заключается, ... Станет ли он сразу не эффективным и почему?

dmz>Да потому, что язык не поддерживает.
А что он должен поддреживать? Только анонимные функции? Т.е. в C# уже на ура будет работать?

dmz>Честно говоря, приплетать опять грасп к этой задаче — это уже слишком.

Они приплетались не к этой задаче, а к вопросу о том с каким самоваром я лезу в фп
Куда, н-р, денешься от того, что есть точки не устойчивости в программах? Надо же как-то с этим бороться. В ООП интерфейсом отгараживаются, в ФП тоже как-то нужно бороться.

dmz> Ну вот пример — был у нас фильтр непустых строк, но, допустим, надо бы отсортировать комментарии и вообще — уметь наложить вместо одного фильтра N фильтров, но фильтры известны в компайл-тайме. Ну, в общем, вполне себе допущение, не хуже уже прочих.


dmz>Решение на ОО (оппа, группового наложения мы сразу не не предусмотрели? Ну, не беда)

dmz>
dmz>class MegaFilter : Filter 
dmz>{
dmz>};
dmz>


Гхм. Это вообще-то называется AndFilter и его добавление не приводит к изменниям в коде. Тот самый OCP в действии
Реализовывать его, пока он не был нужен и было бы овердизайном и заточкой под изменения, которые никогда не случатся (в отличии от декларации интерфейсов).

dmz>Решение три — рефакторим интерфейсы

Давайте без этого

NGG>>то в чём его плюсы перед имеющимся в наличии ООД? Если плюсы есть, то я стану использовать его в повседневной работе и буду рад безмерно.

NGG>>Согласен полностью. Простые. Но я же смог показать на простой задаче применение ОО-дизайна.
dmz>В итоге имеем овердизайн.
+1024.
Только цель предложенного дизайна была не показать как ооп крут, а фп гавно (такое ощущение, что многими, кто мне отвечал, оно воспринимается именно так). В том сообщении я задал вопрос про то, как выглядело бы решение в функциональном стиле будь это не задачка на две строчки, а "реальная" задача. ОО-код служит примером того, как я ответил бы на этот вопрос задай мне его кто-нибудь. Просто иллюстрация, чтобы было понятно о чём я спрашиваю. Похоже канал коммуникации этим приёмом настроить не удалось

dmz> из постановки очевидно, что "принять набор сущностей, отфильтровать, сгруппировать и и отдать консьюмеру" — это не есть сама задача — это решение некоей задачи.

Да как сказать... не очень очевидно

dmz> если задача решается в одну-две строки — то очевидно, если ВДРУГ, когда нибудь, в следующей жизни потребуются некоторые модификации, то две строчки проще выкинуть и написать заново, если уж на то пошло.

Опять +1024, но я уже тоже об этом раз 5 упомянул в своих ответах.

dmz> Как думаете, он будет вникать в весь "повторно использованный код" ? Или проведет "рефакторинг" — т.е. выкинет или забьет, и напишет свое?

Оффтоп:
Вот я сейчас в роли такого человека оказался. Перед глазами три "фреймоврка" для "легкого" построения гуя в одном приложение и все настолько безобразны (7+ лет приложению), что я плачу. Уже давно мечтаю всё выкинуть и написать своё, бо цена рефакторинга почти такая же, но если текущая задача стоит Х, переписать и выполнить текущую задачу "красиво" будет стоить 100*X, если не больше. Приходится закрывать глаза и получать удовльствие
Собственно эта ситуация отчасти поспособствовала тому, что я стал задавать тут глупые вопросы. Стало интересно, что было бы, пиши это приложение в течении 7 лет теже недалёкие люди, но только на функциональном языке. Больше гавна пришлось бы разбирать или меньше?

dmz>Особенно хорошо становится, когда креативных людей больше одного — все читали гамму, лармана и фаулера, там столько паттернов...

Я боюсь, что "креативных людей" в оо-мире не больше, чем было бы в ф-мире стань он мейнстримом, только читали бы не эти книги, а всяких других умных людей

dmz>Я еще раскрою тему.

Жду с нетерпением
Re[15]: Мастер-класс по ФП
От: NotGonnaGetUs  
Дата: 04.01.09 16:12
Оценка:
Здравствуйте, dmz, Вы писали:

dmz>Но смотрю я не с точки зрения разработчика. Смотрю я, допустим, с точки зрения заказчика или аутсорс-менеджера. И вижу, что вместо задачи построения дискового индекса, которую я заказывал, мне впаривают фреймворк для (цитирую)


Вы передёргиваете. В данном случае, и заказчик и исполнитель я. И таких требований у меня нет.
Принять решение насколько нужно обобщать задачу без общения с заказчиком программист не должен. Это вне рамок его компетенции.

dmz>Что мы видим? Для того, что бы данным решением воспользоваться — нужно пользоваться контейнерным (по сути) классом Group (если у нас данные ходят в каком-нибудь обычном List — придется написать адаптер, какой — нибудь свой FromListProcessor, разобраться, чем отличаются Group от Processor с одинаковым интерфейсом (две разные сущности с внешне одинаковым поведением, тесно сцепленные), а потом еще вытащить свои данные из Group.


Неа, нужно будет только написать свой процессор, который будет результат представлять ввиде желаемого вам листа или кренделя с бубликом.
И никаких гвоздей.
К моменту завоевания мира эти реализации уже будет в составе фреймворка

dmz>И это вместо того, что бы тупо в лоб воспользоваться фундаментальными примитивами group, map, filter — которые будут работать с любыми списками (не знаю, как в джаве, но в питоне есть group, filter, map прямо вот в таком виде) — на которые данная задача естественно декомпозируется.


Кстати, да. В отличии от того же c# в java стандартных group,filter,map — нет. Писать самоделки не даёт отсутствие "компактного" синтаксиса для написания лямбд и понижение производительности связанное с копирование списков, что в сумме перевешивает выгоды от наличия таких методов. В 7-ой будут лямбы и, думается мне, обновятся соответствующим образом стандартные библиотеки.
Re[13]: Мастер-класс по ФП
От: NotGonnaGetUs  
Дата: 04.01.09 16:42
Оценка:
Здравствуйте, BulatZiganshin, Вы писали:

NGG>>Целиком и полностью согласен и нисколько с этим не спорю. Я только повторяю заезженную истину о том, что дьявол в деталях.

NGG>>Как делается настройка "и поведения, и данных"?
NGG>>Как повторно использовать "труды" настройки?

BZ>передачей их в качестве параметров

BZ>видимо, речь идёт о partial application?

Сарказм понятен, переформулирую вопрос.
Чтобы передать "в качестве параметров" нужно как-то создать эти парметры.
Как они будут создаваться?
Как избежать дублирования кода, если метод дергается из разных мест в коде со слегка различающимися параметрами?
Не дурак, понимаю, что общая логика должна быть вынесена методы.
Тогда почему в предлагаемых решениях они не вынесены?
Опять не дурак, опять понимаю, что на кой их выносить, если всё в одной строчке.

Ну так я же просил показать как будет выглядеть решение "боевой" задачи, как если бы это было решение, которое ждёт большее будущее и долгая история развития
У меня подозрение, что проделай вы всё это, вместо одной строчки было бы что-то похожее по количеству строчек на код VoidEх'a.

Ну раз этот поинт никому не интересен можно забить на мои вопросы.

BZ>4) нужен двухсторонний автовывод типов с автоматическим обобщением. этого тоже нет, что затрудняет разбиение алгоритмов на множество небольших [глобальных, локальных и анонимных] функций — засребёшься описывать типы всех их параметров и результатов

Кстати, да. Декларации типов с генериками могут засрать код хорошо... Даже без относительно к фп хотелось бы такое иметь в наличии

BZ>удобно ли это? на мой взгляд нет — нужно сгруппировать описание контрола, перевод данных в него и обратно. соответственно ФП-дизайн здесь состоит в создании высокоуровневых контролов, которые несут в себе не только свои данные, но и ассоциированные с ними действия. и тогда программа будет выглядеть так:

BZ>
BZ>runDialog [
BZ>  Editbox [Color := Red, Label := "Speed", Init := (do setText =<< readVar speed), 
BZ>                                           OnOK := (do getText >>= writeVar speed)]
BZ>  SpinButton ....
BZ>]
BZ>


Пока гуй простецкий это работает. Пробовал на практике, для java есть возможность так писать используя Groovy и JavaFX. Но как только действия становятся сложнее, чем простое присвоение значения и когда контролы зависят друг от друга многими связями, плюсов станосится как-то заментно поменьше... mvc опять начинает рулить.

Но подход "dsl средствами языка" мне тоже нравится. Жаль, что в синтаксис java досточно убог для его широкого применения. Жду java7, где обещают быть улучшения в этом плане.
Re[3]: Мастер-класс по ФП
От: NotGonnaGetUs  
Дата: 04.01.09 16:54
Оценка:
Здравствуйте, dmz, Вы писали:

dmz>Слать это в сокет очень полезно, без сомнения. А вот сделаем реалистичное допущение относительно данной задачи в рамках ее предметной области (уж какая есть) — писать в каждый файл только уникальные строки (т.к. на кой черт писать дублирующие?)


dmz>решение на условном ФП:

dmz>concrete_filter = unique . filter filt
dmz>[/code]

Тоже самое на условной java:
class UniqueFilter<T> implements IFilter<T> {
    private Collection<T> uniqueData = ...
    UniqueFilter(IFilter<T> filter) {
       ...
    }
    
    UniqueFilter() {
       ...
    }

    boolean add(T t) {
       ...
    }
}


Re[14]: Мастер-класс по ФП
От: dmz Россия  
Дата: 04.01.09 17:23
Оценка:
NGG>Ну так если затем расписать все перечисленные в аргуметах интерфейсы классы, получится тоже самое количество кода что в обрисованном мной вараинте.

В джаве — да. За нее я и не говорю.

NGG>>>Если расписать всё это, получатся теже яйца, что и в оо варианте вид сбоку


Что в ФП варианте можно расписать? Приводился — ранее — вполне рабочий код.

NGG>СompositeGroup — сам алгоритм


Спорная вещь — засовывания алгоритма в класс, который выглядит как контейнерный...

NGG>Разве не яйца?


И как-то очень много.

NGG>Наверное, очевидным должно быть, что не только для раз и на всегда заданных условий, а для более широго круга задач

NGG>Вот собственно поддтверждения этому я и хочу найти.

Ну какие нужны подтверждения? Когда меняется ситуация, редактировать ФП код как-то проще, поскольку его обычно меньше. Общие вещи, абстракции — выделяются и выносятся. Рассуждать 'в общем' мне тяжело — лучше на конкретных примерах. Вот есть у меня компилятор, который пережил несколько смен синтаксиса и еще переживет. Там нет ничего, что можно было бы унаследовать, и создать, например, новый компилятор путем наследования и подмешивания к существующему. Плохо ли это? Нет, это только хорошо.

NGG>А что он должен поддреживать? Только анонимные функции? Т.е. в C# уже на ура будет работать?


Ну вот, композицию функций, например. Алгебраические типы. Паттерн-матчинг.

dmz>> Ну вот пример — был у нас фильтр непустых строк, но, допустим, надо бы отсортировать комментарии и вообще — уметь наложить вместо одного фильтра N фильтров, но фильтры известны в компайл-тайме. Ну, в общем, вполне себе допущение, не хуже уже прочих.


NGG>Гхм. Это вообще-то называется AndFilter и его добавление не приводит к изменниям в коде. Тот самый OCP в действии


Его написать надо, или его можно сгенерировать из имеющихся фильтров? Или есть конструкция AndFilter<FilterA, FilterB, ...> которая его породит?

NGG>Реализовывать его, пока он не был нужен и было бы овердизайном и заточкой под изменения, которые никогда не случатся (в отличии от декларации интерфейсов).


Ну так impact, который получится при таком изменении требований — он в итоге разный в ФП и ООП?


NGG>Только цель предложенного дизайна была не показать как ооп крут, а фп гавно (такое ощущение, что многими, кто мне отвечал, оно воспринимается именно так). В том сообщении я задал вопрос про то, как выглядело бы решение в функциональном стиле будь это не задачка на две строчки, а "реальная" задача.


Как можно показать решение "не на две строчки" в двухстрочной задаче?

NGG>ОО-код служит примером того, как я ответил бы на этот вопрос задай мне его кто-нибудь. Просто иллюстрация, чтобы было понятно о чём я спрашиваю. NGG>Похоже канал коммуникации этим приёмом настроить не удалось


Ну, наверное, не лучший пример. Не знаю, где взять лучший. Попробую сформулировать свою позже.

NGG>Вот я сейчас в роли такого человека оказался. Перед глазами три "фреймоврка" для "легкого" построения гуя в одном приложение и все настолько безобразны (7+ лет приложению), что я плачу. Уже давно мечтаю всё выкинуть и написать своё, бо цена рефакторинга почти такая же, но если текущая задача стоит Х, переписать и выполнить текущую задачу "красиво" будет стоить 100*X, если не больше. Приходится закрывать глаза и получать удовльствие


Объем задачи другой. Но ведь тут уже ничего не поможет — ни фп, ни ооп.

NGG>Собственно эта ситуация отчасти поспособствовала тому, что я стал задавать тут глупые вопросы. Стало интересно, что было бы, пиши это приложение в течении 7 лет теже недалёкие люди, но только на функциональном языке. Больше гавна пришлось бы разбирать или меньше?


Все также зависит от человеческого фактора. ФП — очень мощный инструмент. В правильных руках — он послужит сокращению сроков и издержек. Но не в тех руках — оно будет только во вред. Преимущество ФП в том, что в нем есть встроенная защита от дурака. Недостаток ФП в том, что дурак, обошедший эту защиту — стоит сотни обычных по разрушительным последствиям.

Инструмент нужно выбирать по задаче. Для меня в моих условиях — ФП является более выгодным инструментом. Для кого-то — вероятно, нет.
Как мне кажется — ФЯ являются более мощными и более высокоуровневыми, по сравнению с распространенными обычными языками.
Re[4]: Мастер-класс по ФП
От: dmz Россия  
Дата: 04.01.09 17:25
Оценка:
dmz>>решение на условном ФП:
dmz>>concrete_filter = unique . filter filt
dmz>>[/code]

NGG>Тоже самое на условной java:


Не говоря о том, что здесь всего больше, мне как-то неочевидно, как это должно использоваться.
Re[5]: Мастер-класс по ФП
От: Аноним  
Дата: 04.01.09 21:00
Оценка:
Здравствуйте, dmz, Вы писали:

dmz>Не говоря о том, что здесь всего больше, мне как-то неочевидно, как это должно использоваться.


вместо new FilterImp(...) ставится new UniqueFilter(new FilterImpl()) и используется. FilterImpl это реализация фильтра, который нужно сделать уникальным...
Re[6]: Мастер-класс по ФП
От: dmz Россия  
Дата: 05.01.09 05:26
Оценка:
А>вместо new FilterImp(...) ставится new UniqueFilter(new FilterImpl()) и используется. FilterImpl это реализация фильтра, который нужно сделать уникальным...

Предложенный интерфейс фильтра подразумевает, что он применяется к одному элементу:

interface IFilter<T>
{
    boolean match(T t);
}


и в реализации так и происходит. Уникальные элементы таким образом не отфильтруешь.
Re[6]: Мастер-класс по ФП
От: geniepro http://geniepro.livejournal.com/
Дата: 05.01.09 05:53
Оценка:
Здравствуйте, NotGonnaGetUs, Вы писали:

NGG>Использование функции c не безопасно — н-р, можно при правке кода поменять a на a', а про b забыть...

NGG>Компилятор промолчит. Может быть даже тесты пройдут успешно, т.к. разница между b и b' крылась в каком-то редком случае почему-то не покрытому при тестировании. А в итоге приложение не отработает как следует в самый ответственный момент.

Ну и что тут необычного? Это постоянно происходит в любых языках и парадигмах -- ФП, очевидно, не является серебрянной пулей.
Конкретно с этой проблемой может быть могут немного уменьшить опасность зависимые типы, когда их сделают простыми для использования. И то не на 100%...

NGG>Возможно моя боязнь блуждающих по аргументам в свободном полёте связных функций это только от недостатка опыта в их готовке...

NGG>Но пока мне кажется, что разумно было бы явно декларировать зависимость между аргументами:
NGG>
NGG>data Converter = Converter 
NGG>{
NGG>     encoder :: (Int->String), 
NGG>     decoder :: (String -> Int)
NGG>}

NGG>c :: Converter -> String(неважно что) -> String (в неважно что)
NGG>

NGG>Из декларации сразу видно что к чему.
NGG>Разве нет?

Ну Вам же никто не запрещает так делать? Нужно -- так делайте! Все возможности для этого у Вас есть...

NGG>Гхм. Тут какое-то расхождение во взглядах на то, что такое монолитность

NGG>Мне как раз твоё решение видится монолитным кусоком кода, а моё комбинируется из нескольких классов для полученя нужного результата и каждый из этих классов может жить счастливой самостоятельной жизнью.

Делайте решение в ФП-стиле -- то есть кучу маленьких функций, максимально обобщённых. И тогда останется просто комбинировать их в нужном порядке -- вот и не будет этой страшной монолитности...
Re[4]: Мастер-класс по ФП
От: Tonal- Россия www.promsoft.ru
Дата: 05.01.09 05:59
Оценка: +1
Здравствуйте, BulatZiganshin, Вы писали:
BZ>как-то всё это сложно
Ну я это и нарисовал. Только использовал списковые дополнения вместо map-ов
Вместо mapMaybe (stripPrefix (make dir)) выписал списковое доплнение через take и drop для случая непустого имени (с монадами пока торможу).
mapSnd — тоже выписал явно.

По ходу есть ещё одно препятствие в изучении ФП, но оно довольно общее для любых языков — пока не очень свободно знаешь язык и его стандартные библиотеки, очень трудно распознать алгоритм в чужом коде.
И тем более предсказать трудоёмкость.
Например, решение через take и drop скорее всего проигрывает по сравнению с mapMaybe (stripPrefix (make dir)). По крайней мере в С/С++ я мог бы это отразить. Но как это в haskell-е я пока не в курсе.
... << RSDN@Home 1.2.0 alpha 4 rev. 0>>
Re[13]: Мастер-класс по ФП
От: geniepro http://geniepro.livejournal.com/
Дата: 05.01.09 06:20
Оценка:
Здравствуйте, BulatZiganshin, Вы писали:

BZ>вот пример. представим классическую gui-библиотеку. реализация диалога настроек на ней может выглядеть так:

BZ>1) создаём все необходимые controls
BZ>2) заполняем их исходными данными
BZ>3) отображаем диалог
BZ>4) после нажатия Ok обновляем переменные

BZ>удобно ли это? на мой взгляд нет — нужно сгруппировать описание контрола, перевод данных в него и обратно. соответственно ФП-дизайн здесь состоит в создании высокоуровневых контролов, которые несут в себе не только свои данные, но и ассоциированные с ними действия. и тогда программа будет выглядеть так:

BZ>
BZ>runDialog [
BZ>  Editbox [Color := Red, Label := "Speed", Init := (do setText =<< readVar speed), 
BZ>                                           OnOK := (do getText >>= writeVar speed)]
BZ>  SpinButton ....
BZ>]
BZ>


Кстати, вот животрепещущая тема (для меня), можете посоветовать что-нить в этом плане? Имею в виду сами принципы построения такого фреймворка применительно к Хаскеллу. Всякие там Model-View-Controller в функциональном стиле...
Re[5]: Мастер-класс по ФП
От: FR  
Дата: 05.01.09 06:25
Оценка:
Здравствуйте, Tonal-, Вы писали:

T>По ходу есть ещё одно препятствие в изучении ФП, но оно довольно общее для любых языков — пока не очень свободно знаешь язык и его стандартные библиотеки, очень трудно распознать алгоритм в чужом коде.


С ФП с этим полегче по моему. Вот при изучении Smalltalk у человека знающего майнстримный ООП приключается когнитивный диссонанс
Re[12]: Мастер-класс по ФП
От: Tonal- Россия www.promsoft.ru
Дата: 05.01.09 07:02
Оценка:
Здравствуйте, NotGonnaGetUs, Вы писали:
dmz>process inp filt group_by consume = inp >>> filter filt >>> group group_by >>> consume

NGG>А если я напишу класс
NGG>сlass TaskNumberOne {
NGG>void process(Source inp, Filter filt , Group group_by, Consumer consume)  {...}
NGG>}

Для того, чтобы код был аналогичен, нужно однако чуть добавить:
сlass TaskNumberOne {
  template <class Source, class Filter, class Group, class Consumer>
  void process(Source inp, Filter filt, Group group_by, Consumer consume)  {...}

Тогда мы можем воспользоваться "трудами" фильтра в группировке, только они должны имеь согласованный интерфейс.
При этом возникает ещё 2 типа — то что получилось после фильтрации и то, что получилось после группировки. Но если попытаться их здесь явно выписать, то получаются уже шаблоны шаблонов и сигнатура получается монстрообразная, а на Java или C# просто невыразимая...
... << RSDN@Home 1.2.0 alpha 4 rev. 0>>
Re[14]: Мастер-класс по ФП
От: geniepro http://geniepro.livejournal.com/
Дата: 05.01.09 07:08
Оценка:
Здравствуйте, NotGonnaGetUs, Вы писали:

NGG>Ну так я же просил показать как будет выглядеть решение "боевой" задачи, как если бы это было решение, которое ждёт большее будущее и долгая история развития

NGG>У меня подозрение, что проделай вы всё это, вместо одной строчки было бы что-то похожее по количеству строчек на код VoidEх'a.

В таких проектах объём документации должен быть на порядок больше объёма кода, иначе проблемы будут всегда. И комментарии в коде должны быть обширными. И вапще Literate Programming тут уже напрашивается.
В итоге подходим к тому, что программа должны быть исполняемым руководством...
Re[7]: Мастер-класс по ФП
От: deniok Россия  
Дата: 05.01.09 08:14
Оценка:
Здравствуйте, geniepro, Вы писали:

G>Здравствуйте, NotGonnaGetUs, Вы писали:


NGG>>Использование функции c не безопасно — н-р, можно при правке кода поменять a на a', а про b забыть...

NGG>>Компилятор промолчит. Может быть даже тесты пройдут успешно, т.к. разница между b и b' крылась в каком-то редком случае почему-то не покрытому при тестировании. А в итоге приложение не отработает как следует в самый ответственный момент.

G>Ну и что тут необычного? Это постоянно происходит в любых языках и парадигмах -- ФП, очевидно, не является серебрянной пулей.

G>Конкретно с этой проблемой может быть могут немного уменьшить опасность зависимые типы, когда их сделают простыми для использования. И то не на 100%...

Почему не на 100%? Если использовать технику наподобие доступной в COQ, когда одновременно генерируется программа на Хаскеле или Камле и доказательство корректности всех ее требуемых инвариантов, то таки на 100% Правда назвать использование COQ простым язык не поворачивается
Re[8]: Мастер-класс по ФП
От: geniepro http://geniepro.livejournal.com/
Дата: 05.01.09 09:31
Оценка:
Здравствуйте, deniok, Вы писали:

D>Почему не на 100%? Если использовать технику наподобие доступной в COQ, когда одновременно генерируется программа на Хаскеле или Камле и доказательство корректности всех ее требуемых инвариантов, то таки на 100% Правда назвать использование COQ простым язык не поворачивается


Так вот тут-то и проблема -- указать все инварианты просто нереально, всегда появятся новые требования, и где-то что-то будет забыто. А значит и доказательство соответствия инвариантам не будут 100%-полными...
Re[9]: Мастер-класс по ФП
От: deniok Россия  
Дата: 05.01.09 09:46
Оценка:
Здравствуйте, geniepro, Вы писали:


G>Так вот тут-то и проблема -- указать все инварианты просто нереально, всегда появятся новые требования, и где-то что-то будет забыто. А значит и доказательство соответствия инвариантам не будут 100%-полными...


Не, ну если ты забыл запрограммировать новые требования или откомпилировать их, то да. Но если тебе удалось откомпилировать программу на COQ, то результирующий хаскелловский код удовлетворяет всем инвариантам, при этом формальное доказательство этого факта лежит рядом.
Re[7]: Мастер-класс по ФП
От: Аноним  
Дата: 05.01.09 11:03
Оценка: :)
Здравствуйте, dmz, Вы писали:

dmz>Уникальные элементы таким образом не отфильтруешь.


Упражнение для первого класса...
class UniqueFilter<T> implements IFilter<T> {
    private Collection<T> uniqueData = new HashSet();
    private IFilter<T> filter;
    UniqueFilter(IFilter<T> filter) {
       this.filter = filter;
    }
    
    boolean match(T t) {
       if (uniqueData.contains(t)) {
           return false;
       }
       if (filter.match(t)){ 
           uniqueData.add(t);
           return true;
       }
       return false;
    }
}
Re[8]: Мастер-класс по ФП
От: dmz Россия  
Дата: 05.01.09 12:02
Оценка:
dmz>>Уникальные элементы таким образом не отфильтруешь.

А>Упражнение для первого класса...


Да, как-то уже варианты с побочными эффектами фильтруешь бессознательно. Минуса два — первый —
накопление элементов в фитре (по сути — предикате), в там, где мы совсем не ожидаем.

Второй — если нам потребуется операция, которая обрабатывает всю коллекцию — мы можем или дальше
хачить фильтр — т.е. в первый проход накапливаем, во второй фильтруем — или вводить новую сущность — собственно,
сам процесс фильтрации, интерфейс, класс и вставлять его в цепочку операций. Делать в фильтре — фейл дизайна,
вводить новую сущность — куча писанины для единичной элементарной операции.
Re[5]: Мастер-класс по ФП
От: BulatZiganshin  
Дата: 05.01.09 12:18
Оценка:
Здравствуйте, Tonal-, Вы писали:

BZ>>как-то всё это сложно

T>Ну я это и нарисовал. Только использовал списковые дополнения вместо map-ов

в чём для меня существенная разница. ты это рассматриваешь в том стиле, который я считаю императивным: у нас есть переменные, мы выполняем над ними какие-то операции и получаем результат. я действую в стиле — у нас есть входное значение и выходное, мы описываем преобразование из одного в другое, и именно это я считаю ag-подходом к описанию алгоримтов: любая решаемая задача сводится к преобразованию данных, которое мы описываем через декомпозицию вплоть до элементарных операций

T>И тем более предсказать трудоёмкость.

T>Например, решение через take и drop скорее всего проигрывает по сравнению с mapMaybe (stripPrefix (make dir)). По крайней мере в С/С++ я мог бы это отразить. Но как это в haskell-е я пока не в курсе.

меньше всего интересует. в большинстве случаев неважно, в остальных это только один из аспектов оптимизации
Люди, я люблю вас! Будьте бдительны!!!
Re[7]: Мастер-класс по ФП
От: thesz Россия http://thesz.livejournal.com
Дата: 05.01.09 15:19
Оценка: :)
NGG>>Компилятор промолчит. Может быть даже тесты пройдут успешно, т.к. разница между b и b' крылась в каком-то редком случае почему-то не покрытому при тестировании. А в итоге приложение не отработает как следует в самый ответственный момент.

G>Ну и что тут необычного? Это постоянно происходит в любых языках и парадигмах -- ФП, очевидно, не является серебрянной пулей.


"Серебрянная пуля" имеет вполне определенный смысл — это уменьшение труда программиста.

А это — панацея.
Yours truly, Serguey Zefirov (thesz NA mail TOCHKA ru)
Re[2]: Итог размышлнеий
От: NotGonnaGetUs  
Дата: 06.01.09 16:37
Оценка: 7 (2) +1
Здравствуйте, NotGonnaGetUs, Вы писали:

BZ>> есть список слов. надо записать в файл "a" все слова, начинающиеся на букву 'a', в файл "b" — слова на 'b' и т.д. вплоть до z


NGG>После чего глубоко задумался.


NGG>Прежде всего обобщил бы постановку задачи:


NGG>Дано: источник данных, критерий разбиения данных на группы

NGG>Надо: данные разбить на группы, обработать данные в группах.

NGG>Может кто-нибудь из "гуру" набросать, как будет выглядеть "обобщённое" решение 1-ой задачки в функциональном стиле?


Вернулся к обсуждению имевшему место быть какое-то время назад на этом форуме и нашёл слова thesz'a о том, что в цепочке
дизайн -> реализация -> отладка использование фя позволяет упростить две последнии стадии, а с первой могут помочь формальные спецификации.

Посмотрел на нашу битву реализаций и решил-таки (благо новый год и есть свободное время) познакомиться с одним из языков формальных спецификаций (Z) поближе, что в итоге позволило взглянуть на поднятую тему под немного другим углом.

Прежде всего, предложенное обобщение слишком "обобщенное": для того, чтобы перейти к дизайну, а затем реализации, требуется принять ряд решений (наложить ограничения) от которых итоговое решение довольно сильно зависит. Эти ограничения не имеют никакой связи с языком программирования или "стилем" проектирования принятым в этом языке.

Вернёмся к задаче в исходной постановке.

Её можно разбить на подзадачи разными способами.
Н-р:
1)
— Получить из списка строк Х список строк, в котором все слова начинаются с буквы Y (filterByFirstLetter :: [String] -> Char -> [String]).
— Записать в файл с именем X список слов Y (write :: String -> [String] -> IO())
Тогда итоговое решение выглядит так:
mapM_ (\letter -> write [letter] (filterByFirstLetter theData letter)) ['a'..'z']
2)
— Записать в файл с именем Х все слова из списка Y начинающиеся с префикса Х (writeWithPrefix :: String -> [String] -> IO())
Тогда итоговое решение выглядит так:
mapM_ (\letter -> writeWithPrefix [letter] theData) ['a'..'z']
3)
— Реализовать (или использовать существующую) инфраструктуру mailbox'ов.
— Реализовать компонент, который получая из mailbox'a слово пишет его в файл с именем Х.
— Реализовать компонент, который получая из mailbox'a слова пересылает их по первой букве в другие mailbox'ы используя маппинг (буква -> mailbox)
Тогда итоговое решение выглядит так (мифический синтаксис):
mapping = map (\letter -> (letter, makeFileMailBox [letter])) ['a' .. 'z']
dispatcher = makeDispatcherMailBox mapping
mapM_ (\word -> dispatch dispatcher word) theData

И т.п.

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

Коль скоро это так, возникает закономерный вопрос: а какое из решений лучше?
То что короче или то что понятнее или то что создаёт больше повтроноиспользуемых артефактов или то что производительнее или то что использует больше существующих артефактов (и потому быстрее реализуется) или или или ...

Вот тут основная загвоздка и находится, т.к.
— Чтобы ответить на этот вопрос нужно знать контекст, в котором происходит оценка (является ли данная задача самодостаточной или частью общей задачи, какова вероятность внесения изменений в постановку задачи и какого рода будут эти изменения и т.д. и т.п.)
— Часть критериев качества определяется ой-как не формально и потому существует больше пространство для жарких споров

Раз нет общепринятых формальных критериев, появляются эвристики выражаемые в той или иной форме.

Н-р, в ОО-мире — это разнообразные шаблоны (gof++) — стандартные решения с описанием их плюсов и минусов, антишаблоны — примеры часто встречающихся плохих решений ,"запахи" — характеристики кода, который потенциально (но не обзяательно) приведёт к проблемам в будущем (трудность поддрежки), всякие разные принципы — LSP, OCP, закон деметры и т.п.
(Оффтоп: эти эвристики иногда принимают за истину в поледней инстанции, убеждаются, что это не так и начинают хаять по чём зря, вместо того, чтобы разобраться когда и где они работают (Имхо, из всего зоопарка нужно знать только шаблоны grasp, т.к. они, как аксиомы в геометрии, дают базис на основе которого можно вывести практически любую известную "эвристику" и получить представление о том, когда ей стоит следовать, а когда нет)).

Т.к. необдуманное следование эвристикам ведёт к противоположному от ожидаемого результату и т.к. переделка всегда сопряжена с рисками (наплодить новых багов + потратить кучу времени без осязаемого полезного выхода) популярностью пользуются подходы:
"раз работает — не тронь" и "использовать первое пришедшее в голову решение" (или его более интеллектуальные вариант — "everything should be made as simple as possible, but no simpler."). При этом разрешение рисков откладывается на будущее, на момент когда они "стрельнут". И практика показывает, что такой подход к разработке, хоть и не без греха, но имеет право на жизнь.

Причём, насколько мне позволяет судить опыт (и обрывки информации почерпнутые, в том числе, на форумах rdsn) результативность следования "заветам простоты" при программировании с использованием ФЯ выше, чем если используется какой-либо из императивных языков. Причин вижу две:
— актвиное использование неизменяемых структур данных минимизирует появление сайд эффектов и, как следствие, приводит к решениям с меньшей связностью (значит вносить изменения и тестировать код существенно проще).
— лаконичный синтаксис + декларативный стиль описания алгоритмов позволяет писать достаточно крупные фрагменты кода не заботясь о его будщей судьбе, т.к. цена внесения в него измнений и переписывания "с нуля" становится сопоставимой.

(Как будет работать ФП при увеличении сложности системы — не знаю.
Время потраченное на дизайн, наверняка, будет сопоставимым со временем потраченным на дизайн в ОО-стиле.
Время потраченное на реализанию меньше, чем в случае "многословных" языков без вывода типов и синтаксического сахара, но не думаю, что на порядок (у "многословных" языков есть чудо-IDE в рукаве (с автоматическими рефакторингами, отладчиками, профайлерами и т.п.) и обширная база "готового кода" — библиотек и фреймворков, a у фя больше плясок с достижением желаемой производительности).
На внесение изменений — хз, зависит от того, насколько был хорош тот и другой дизайны.)

Но что-то я слегка отклонился от главной темы. Возвращаюсь.

Да, формально оценить качество решения без контекста невозможно. А субъективные оценки интересны обычно только самим субъектам

Исходя из этого моё исходное желание увидеть "промышленный" функциональный подход в действии на примере задачки про файлы было обречено не сбыться, т.к. сила фп как раз в том, чтобы быть всегда kiss, т.е. не обобщать без нужды

И тем не менее! Буду отвечать себе сам

Допустим я при реализации выбрал решение 1) т.к. решение 1 в 1 совпадает с постановкой задачи.
Потом мне говорят — надо ещё уметь по первым трём буквам строить файлы.
Я, следуя "заветам" пишу:
theData = ["abc", "def", "ijk", "axxx", "der", "preved", "%%%%%", "12345"]

type Discriminator a b = (a -> b)
type Group a b = (a, [b])
type GroupAction a b c = (Group a b -> c) 

groupByF :: Ord b => Discriminator a b -> [a] -> [Group b a]
groupByF discriminator list = 
    list >>== sortByF discriminator >>== groupByF discriminator >>== map (\list -> (discriminator $ head list, list))

filterByD :: (a -> Bool) -> [Group a b] -> [Group a b]
filterByD f = filter (\(groupId,_) -> f groupId)

writeConsole :: GroupAction String String (IO())                                              
writeConsole (groupId, []) = return ()
writeConsole (groupId, listOfWords) = putStrLn $ groupId ++ " -> " ++ (show listOfWords)

(>>==) :: a -> (a -> b) -> b
(>>==) x f = f x   

sortByF f = sortBy (\x y -> (f x) `compare` (f y))  
groupByF f = groupBy (\x y -> (f x) == (f y))  

main = theData 
       >>== groupByF (take 3) 
       >>== filterByD ((`elem` [x| x <- ['a'..'z']]).head)
       >>== mapM_ writeConsole

Ну, переписал всё и ладно.
А тут просят: а теперь решено формировать файлы не по префиксу, а по "содержит".
Опа... опять не получается простым движением руки изменение внести, т.к. функция дикриминатор превратилась в список дикриминаторов, причём с другой сигнатурой.

Опять переписывать groupByF?

А если в следующий раз попросят от 'a' до 'n' по префиксу писать, а от 'o' до 'z' по "содержит"?
Ешкин кот — ни старый groupByPrefix, ни новый groupByContains уже не используешь — опять писать что-то нужно.

Ну напишем:

theData = ["abc", "def", "ijk", "axxx", "der", "preved", "%%%%%", "12345", "zoo"]

type Group = (String, [String])
type GroupAction a = (Group -> a) 

data Discriminator = DByPrefix String | DByContains String | DByPostfix String

match text (DByPrefix prefix) = isPrefixOf prefix text 
match text (DByContains prefix) = isInfixOf prefix text 
match text (DByPostfix prefix) = isSuffixOf prefix text 

groupId (DByPrefix prefix) = prefix 
groupId (DByContains prefix) = prefix 
groupId (DByPostfix prefix) = prefix 

groupByD :: [Discriminator] -> [String] -> [Group]
groupByD ds ws = (groupByD' ds ws) 
                 >>== sortByF fst >>== groupByF fst 
                 >>== map (\x -> (fst $ head x, map snd x)) 
    where 
        groupByD' ds [] = []  
        groupByD' ds (w:ws) = case find (match w) ds of
                                    Just x ->  (groupId x, w) : (groupByD' ds ws)
                                    Nothing ->groupByD' ds ws   
                
filterByD :: (String -> Bool) -> [Group] -> [Group]
filterByD f = filter (\(groupId,_) -> f groupId)

writeConsole :: GroupAction (IO())                                              
writeConsole (groupId, []) = return ()
writeConsole (groupId, listOfWords) = putStrLn $ groupId ++ " -> " ++ (show listOfWords)

(>>==) :: a -> (a -> b) -> b
(>>==) x f = f x   

sortByF f = sortBy (\x y -> (f x) `compare` (f y))  
groupByF f = groupBy (\x y -> (f x) == (f y))  

main = theData 
       >>== groupByD ((map DByPrefix [[x]| x <- ['a'..'n']]) ++ (map DByContains [[x]| x <- ['o'..'z']])) 
       >>== filterByD ((`elem` [x| x <- ['a'..'z']]).head) 
       >>== mapM_ writeConsole


А тут говорят, хочу слова начинающиеся в префикса 'a' писать в консоль, а содрежащие 'a' или начинающиеся с 'b' в файл и имя файла сам хочу выбрать...

Скрепя сердцем делаю:
theData = ["abc", "def", "ijk", "axxx", "der", "preved", "%%%%%", "12345", "zoo"]

--- IFilter and implementations?
data Discriminator = DByPrefix String | DByContains String | DByPostfix String

matchD text (DByPrefix prefix) = isPrefixOf prefix text 
matchD text (DByContains prefix) = isInfixOf prefix text 
matchD text (DByPostfix prefix) = isSuffixOf prefix text 

discrId (DByPrefix prefix) = prefix 
discrId (DByContains prefix) = prefix 
discrId (DByPostfix prefix) = prefix 

--- IProcessor and implementations ?
type GroupAction = (Group -> IO()) 

makeConsoleAction1 :: String -> GroupAction 
makeConsoleAction1 _ (_, []) = return ()
makeConsoleAction1 name (_, listOfWords) =  putStrLn $ name ++ " (prefix)-> " ++ (show listOfWords)

makeConsoleAction2 :: String -> GroupAction 
makeConsoleAction2 _ (_, []) = return ()
makeConsoleAction2 name (_, listOfWords) =  putStrLn $ name ++ " (contains)-> " ++ (show listOfWords)
  
writeConsole :: GroupAction                                              
writeConsole (_, []) = return ()
writeConsole (groupInfo, listOfWords) = putStrLn $ (groupId groupInfo) ++ " -> " ++ (show listOfWords)


--- IGroup and implementations ?
data GroupInfo = FilteredGroup GroupAction Discriminator

discriminator (FilteredGroup _ d) = d
action (FilteredGroup a _) = a
    
match text groupInfo = matchD text $ discriminator groupInfo 
groupId groupInfo = discrId $ discriminator groupInfo 

--- mutable (in oo) state of IGroup or IProcessor ... 
type Group = (GroupInfo, [String])

processGroup (groupInfo, listOfWords) = (action groupInfo) (groupInfo, listOfWords)

--- group builder...
groupByD :: [GroupInfo] -> [String] -> [Group]
groupByD ds ws = (groupByD' ds ws) 
                 >>== sortByF (groupId.fst) >>== groupByF (groupId.fst) 
                 >>== map (\x -> (fst $ head x, map snd x)) 
    where 
        groupByD' ds [] = []  
        groupByD' ds (w:ws) = case find (match w) ds of
                                    Just info -> (info, w) : (groupByD' ds ws)
                                    Nothing -> groupByD' ds ws   
                
filterByD :: (String -> Bool) -> [Group] -> [Group]
filterByD f = filter (\(groupInfo,_) -> f $ groupId groupInfo)

(>>==) :: a -> (a -> b) -> b
(>>==) x f = f x   

sortByF f = sortBy (\x y -> (f x) `compare` (f y))  
groupByF f = groupBy (\x y -> (f x) == (f y))  

--- solution itself
main = theData 
       >>== groupByD groupInfos  
       >>== filterByD ((`elem` [x| x <- ['a'..'z']]).head)  
       >>== mapM_ processGroup
    where        
       groupInfos = (map (\d -> FilteredGroup (makeConsoleAction1 (discrId d)) d) (map DByPrefix [[x]| x <- ['a'..'n']]))
                   ++ (map (\d -> FilteredGroup (makeConsoleAction2 (discrId d)) d) (map DByContains [[x]| x <- ['g'..'z']]))

... и видим как близко решение оказывается к глупому оо.

А когда замечаем, что между делом потерялся способ сгруппировать по первым нескольким буквам ... становится ясно, что ещё копать и копать до финала. Слёта даже не могу придумать как это более или менее красиво реализовать четвёртый тип дискриминатора (DByLength Int)

За сим откланиваюсь, будет критика пишите.

з.ы.
К дискуссии о "промышленном" решении можно будет вернуться, когда ФП окажется в массах и массы, пробежавшись по всем граблям n-раз, обобщат их до шаблонов, "запахов" и приниципов

з.з.ы.
Тому кто осилил — респект.
Re: Мастер-класс по ФП
От: subdmitry Россия  
Дата: 06.01.09 22:25
Оценка: :))
Здравствуйте, BulatZiganshin, Вы писали:

BZ>я часто сталкиваюсь с двумя точками зрения — первая "я знаком с ФП, но оно никакой выгоды в программировании не даёт", и вторая "я знаком с ФП, но так и не научился использовать его для повышения эффективности программирования". собственно, это одно и то же, разница только в том, на кого (ФП или самого человека) возлагается вина за отсутствие результата


Еще одно мнение в копилку — ФП дает выигрыш на узком классе задач, связанных со сложной работой с динамическими структурами данных (списками, деревьями, etc) — на что оно, собственно, и заточено. А на том, что типично пишут программисты, оно особенно не нужно.

Кстати, давно хочу полюбоваться, как на Хаскелле запишется следующий императивный код:
void DoIt() {
  A++;
  B[C].D*=10;
  F(G).H=0;
}

Хаскеллисты что-то отказываются решать такую задачу.

Серьезно хочу, не только чтобы какие-то там языки унизить — свои-то минусы можно найти у любого языка.
And if you listen very hard the alg will come to you at last.
Re[2]: Мастер-класс по ФП
От: deniok Россия  
Дата: 06.01.09 22:33
Оценка: +1 :)
Здравствуйте, subdmitry, Вы писали:


S>Кстати, давно хочу полюбоваться, как на Хаскелле запишется следующий императивный код:

S>
S>void DoIt() {
S>  A++;
S>  B[C].D*=10;
S>  F(G).H=0;
S>}
S>

S>Хаскеллисты что-то отказываются решать такую задачу.

Это не задача. Это выглядящие бессмысленными упражнения с записыванием значений в непонятные участки памяти, очевидно, доступные глобально.
Re[3]: Мастер-класс по ФП
От: subdmitry Россия  
Дата: 06.01.09 22:40
Оценка:
Здравствуйте, deniok, Вы писали:

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


Я считаю, что начинать надо с малого. Лично мне не очень понятно, как добиться на Хаскелле решения такой простой проблемы и (серьезно!) хочу его увидеть.

Оно не такое уж и бессмысленное. У меня при программировании часто возникает код, подобный написанному. Хочу понять, как получить его аналог в функциональных языках.

Некоторые тут, кстати, заявляют, что Хаскель лучший императивный язык...
And if you listen very hard the alg will come to you at last.
Re[4]: Мастер-класс по ФП
От: deniok Россия  
Дата: 06.01.09 23:09
Оценка: +3
Здравствуйте, subdmitry, Вы писали:

S>Здравствуйте, deniok, Вы писали:


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


S>Я считаю, что начинать надо с малого. Лично мне не очень понятно, как добиться на Хаскелле решения такой простой проблемы и (серьезно!) хочу его увидеть.


S>Оно не такое уж и бессмысленное. У меня при программировании часто возникает код, подобный написанному. Хочу понять, как получить его аналог в функциональных языках.


Если у тебя возникает такой код, то это очень плохо даже в императивных языках. У тебя там семь идентификаторов, смысл которых неизвестен в рамках данной функции. Такая связность по данным DoIt с внешним миром бессмысленна. Даже в обычном процедурном программировании считается предпочтительным, чтобы внешний мир общаелся с функцией через ее аргументы, а не через глобальные данные.
Re[5]: Мастер-класс по ФП
От: subdmitry Россия  
Дата: 06.01.09 23:36
Оценка: -1 :))
Здравствуйте, deniok, Вы писали:

S>>Оно не такое уж и бессмысленное. У меня при программировании часто возникает код, подобный написанному. Хочу понять, как получить его аналог в функциональных языках.


D>Если у тебя возникает такой код, то это очень плохо даже в императивных языках. У тебя там семь идентификаторов, смысл которых неизвестен в рамках данной функции. Такая связность по данным DoIt с внешним миром бессмысленна.


Да нормальная связанность. Предположим, что у нас есть функция на 100 строк, три из которых что-то меняют в глобальном состоянии программы. Предположим, что я привел только эти три строчки и хочу узнать, как их реализовать.

Если тебе нужен пример из реальной жизни, представь себе download manager (качалку файлов), который при получении очередной порции данных по сети изменяет счетчик всех скачаных байт за сеанс, изменяет количество скачанных байт у данного файла и изменяет количество скачанных байт у данного типа (группы) файлов, который определяется для данного файла посредством вызова функии. Вот тебе и три воздействия на глобальные переменные в одной функции (уж не будем зацикливаться на деталях, что там за операции именно используются).

Для полного прикола можно еще представить, что такие функции вызываются в каждом независимо работающем треде, обслуживающем скачку одного из нескольких одновременно качающихся файлов, так что передавать состояние системы в функцию и получать его назад измененным не получится.

D>Даже в обычном процедурном программировании считается предпочтительным, чтобы внешний мир общаелся с функцией через ее аргументы, а не через глобальные данные.


Не разделяю этих религиозных воззрений. Даже люди, разрабатывающие функциональный подход, признают, что у программы может быть состояние. Ну а доступ к состоянию удобно реализовывать через глобальные переменные — клинический факт. Просто так получается намного короче.

Короче надо понимать ситацию так, что реализация такой вещи на Хаскелле настолько страшна, что ее никто не приведет.
And if you listen very hard the alg will come to you at last.
Re[3]: Итог размышлнеий
От: Beam Россия  
Дата: 07.01.09 01:21
Оценка: 19 (2)
Здравствуйте, NotGonnaGetUs, Вы писали:

BZ>>> есть список слов. надо записать в файл "a" все слова, начинающиеся на букву 'a', в файл "b" — слова на 'b' и т.д. вплоть до z

NGG>>Прежде всего обобщил бы постановку задачи:
NGG>>Дано: источник данных, критерий разбиения данных на группы
NGG>>Надо: данные разбить на группы, обработать данные в группах.
NGG>> как будет выглядеть "обобщённое" решение 1-ой задачки в функциональном стиле?

import Data.List
import Data.Ord

-- РЕШЕНИЕ

-- универсальная функция, группирует список пар (ключ, значение) по ключу, возвращая список пар (ключ, [значения для этого ключа])
-- например groupByFst [(1,2), (1,3), (2,5), (1,4), (2,6)] возвращает [ (1,[2,3,4]), (2,[5,6]) ]
groupByFst ps = [ (fst $ head x, map snd x) | x <- groupBy (\a b -> fst a == fst b) $ sortBy (comparing fst) ps]

-- группируем слова по месту назначения на основе fun (слово -> список куда записать), получаем список пар (место назначения, слова)
-- затем вызываем обработчик process (куда записать -> слова -> IO) для каждой такой пары
processWords fun process words = mapM_ write $ groupedWords
  where
    write (target, words) = process target words
    groupedWords = groupByFst $ [ (target, word) | word <- words, target <- fun word]

-- ИСПОЛЬЗОВАНИЕ

data Target = Console | File String | TempFile deriving (Eq, Ord, Show)

process :: Target -> [String] -> IO ()

process (File filename) words = do
  putStrLn $ "To file " ++ filename ++ " : "
  mapM_ putStrLn words

process Console words = do
  putStrLn $ "To console: "
  mapM_ putStrLn words

process TempFile words = do
  putStrLn $ "To temp file: "
  mapM_ putStrLn words

-- записываем слово в файл в соответствие с первой буквой слова
targets word = [File $ take 1 word ++ ".txt", Console] 

-- записываем слово в файл в соответствие с тремя первыми буквами слова
targets2 word = [File $ take 3 word ++ ".txt", Console] 

-- записываем слово в файл "?.txt" если слово содержит букву ?
-- дополнительно слова, длиной 3 пишем во временный файл
-- дополнительно слова, начинающиеся с гласной буквы пишем в консоль
targets3 word = byContain word ++ byLen3 word ++ byVowel word
  where
    byContain letters = [File $ s:".txt" | s <- letters]
    byLen3 letters | length letters == 3 = [TempFile]
                   | otherwise = []
    byVowel letters | head letters `elem` ['e','u','i','o','a'] = [Console]
                    | otherwise = []

-- MAIN

sourceData = ["test1", "test2", "aaa", "aaz", "aabb", "happy", "new", "year"]

main = do
  processWords targets3 process sourceData


Непосредственно обработкой занимается processWords (универсальное решение)
Обработку мы настраиваем двумя функциями (передаются в качестве параметра processWord):
1. targets — по имени файла говорит, куда этот файла надо записать (в консоль, в конкретный файл, никуда)
2. process — выполняет саму обработку группы слов
На самом деле, то же можно повторить и в ООП Разный подход будет в "настройке".
Думаю решение на ООП будет выглядеть чуть по сложнее, т.к., на мой взгляд, функцию легче охватить взглядом и понять, чем класс ООП.
Не говоря уже о добавлении нескольких классов / интерфейсов.

NGG>Исходя из этого моё исходное желание увидеть "промышленный" функциональный подход в действии на примере задачки про файлы было обречено не сбыться, т.к. сила фп как раз в том, чтобы быть всегда kiss, т.е. не обобщать без нужды


В любом случае "промышленный" код будет потяжелее.
Скормил программке файл на 130 мегов, подавилась, пришлось переписать без сортировки, с чтением файлов по-строчно, хранением хэндлов и т.п.

А из отличий ФП и ООП я бы выделил то, что в ФП легче искать и выделять абстракции. А также легче читать (и писать) программу, т.к. работаешь с меньшим количество кода. И не потому, что ФП лаконичнее, а скорее потому, что независимые куски кода получаются значительно короче.
Best regards, Буравчик
Re[2]: Мастер-класс по ФП
От: yumi  
Дата: 07.01.09 03:30
Оценка:
Здравствуйте, subdmitry, Вы писали:

S>Кстати, давно хочу полюбоваться, как на Хаскелле запишется следующий императивный код:

S>
S>void DoIt() {
S>  A++;
S>  B[C].D*=10;
S>  F(G).H=0;
S>}
S>

S>Хаскеллисты что-то отказываются решать такую задачу.

Оно и понятно, за такое тебе и грамотные императивщики по башке надают.

S>Серьезно хочу, не только чтобы какие-то там языки унизить — свои-то минусы можно найти у любого языка.


Пойми я тоже не хочу никого унизить, просто тут уже сразу видно невооруженным взглядом, отсутствие какой-либо базы, а без базы, тебе никто не сможет даже объяснить, почему ты не прав.
Lisp is not dead. It’s just the URL that has changed:
http://clojure.org
Re[4]: Мастер-класс по ФП
От: VoidEx  
Дата: 07.01.09 03:52
Оценка:
Здравствуйте, subdmitry, Вы писали:

S>Я считаю, что начинать надо с малого. Лично мне не очень понятно, как добиться на Хаскелле решения такой простой проблемы и (серьезно!) хочу его увидеть.


Я бы советовал начинать сразу с throw или longjmp

S>Оно не такое уж и бессмысленное. У меня при программировании часто возникает код, подобный написанному. Хочу понять, как получить его аналог в функциональных языках.


Это плохо, что возникает код, который лезет в какие-то глобальные данные
Re[3]: Мастер-класс по ФП
От: subdmitry Россия  
Дата: 07.01.09 04:06
Оценка:
Здравствуйте, yumi, Вы писали:

Y>Пойми я тоже не хочу никого унизить, просто тут уже сразу видно невооруженным взглядом, отсутствие какой-либо базы, а без базы, тебе никто не сможет даже объяснить, почему ты не прав.


А при чем тут вообще прав/неправ? Был приведен пример реальной задачи (download manager), в которой возникает код, аналогичный привденному. Я не спрашиваю, прав я или неправ, я предлагаю привести решение этой задачи на Хаскелле. Особенно интересно в многопоточном варианте.

Если, конечно, наличествующая у вас база это позволяет сделать, а не просто утверждать, что "глобальные переменные это плохо".
And if you listen very hard the alg will come to you at last.
Re[5]: Мастер-класс по ФП
От: subdmitry Россия  
Дата: 07.01.09 04:18
Оценка:
Здравствуйте, VoidEx, Вы писали:

VE>Это плохо, что возникает код, который лезет в какие-то глобальные данные


О! Вот и продемонстрируйте, как можно на функциональном языке прекрасно без этого обойтись. Пример в студии.
And if you listen very hard the alg will come to you at last.
Re[6]: Мастер-класс по ФП
От: VoidEx  
Дата: 07.01.09 04:28
Оценка:
Здравствуйте, subdmitry, Вы писали:

S>Если тебе нужен пример из реальной жизни, представь себе download manager (качалку файлов), который при получении очередной порции данных по сети изменяет счетчик всех скачаных байт за сеанс, изменяет количество скачанных байт у данного файла и изменяет количество скачанных байт у данного типа (группы) файлов, который определяется для данного файла посредством вызова функии. Вот тебе и три воздействия на глобальные переменные в одной функции (уж не будем зацикливаться на деталях, что там за операции именно используются).

Зачем этим должна заниматься функция получения данных? А если фильтр изменится (на группы файлов)? Это глупо само по себе. А так, функция принимает данные, возвращает кол-во прочитанных байт, которые прибавляются к текущим прочитанным вообще, для данного файла и для группы. Можешь их потом на экран вывести, если хочешь.

S>Для полного прикола можно еще представить, что такие функции вызываются в каждом независимо работающем треде, обслуживающем скачку одного из нескольких одновременно качающихся файлов, так что передавать состояние системы в функцию и получать его назад измененным не получится.


Вот вам полный прикол:
-- Сюда будем писать, сколько байт прочитано
type ByteReads = Chan (Int -> Int)

-- Кушает пользовательский ввод. На 1 - пишет "прочитано 10 байт", на 0 - выход
writer :: ByteReads -> IO ()
writer chan = loop
    where
        loop = getChar >>= command
        command '0' = printf "done\n"
        command '1' = writeChan chan (+10) >> loop
        command '\n' = loop -- ignore
        command c = printf "Illegal: %c\n" c >> loop

-- 1000 раз непрерывно пишет "прочитано cnt байт"
randomWriter chan cnt = do
    mapM_ (writeChan chan) $ take 1000 $ repeat (+cnt)

-- Выводит статистику, сколько байт прочитано
reader :: Int -> [Int -> Int] -> IO ()
reader s xs = mapM_ (printf "Received %d bytes\n") $ scanl (\v op -> op v) s xs

main :: IO ()
main = do
    chan <- newChan :: IO ByteReads
    s <- getChanContents chan
    w1 <- forkIO $ reader 0 s -- Читатель
    w2 <- forkIO $ randomWriter chan 1 -- Пишет 1000 раз по 1 байту
    w3 <- forkIO $ randomWriter chan 2 -- по 2
    w4 <- forkIO $ randomWriter chan 5 -- по 5
    writer chan -- Команды от пользователя


Кстати, я думал, что в Chan может быть один писатель, но не падает. Везёт, или писателей таки может быть куча?

S>Короче надо понимать ситацию так, что реализация такой вещи на Хаскелле настолько страшна, что ее никто не приведет.

Да нет, вон она как проста, когда надо не изменить 5 глобальных переменных в массивах, а решить саму задачу.
Re[6]: Мастер-класс по ФП
От: VoidEx  
Дата: 07.01.09 04:29
Оценка:
Здравствуйте, subdmitry, Вы писали:

S>Здравствуйте, VoidEx, Вы писали:


VE>>Это плохо, что возникает код, который лезет в какие-то глобальные данные


S>О! Вот и продемонстрируйте, как можно на функциональном языке прекрасно без этого обойтись. Пример в студии.


Написал, пожалста
Re[7]: Мастер-класс по ФП
От: VoidEx  
Дата: 07.01.09 04:48
Оценка:
Здравствуйте, VoidEx, Вы писали:

Я там на самом деле лишь интереса ради в Chan писал Int -> Int. Лучше писать сами считанные байты, но не суть.
Re[2]: Мастер-класс по ФП
От: thesz Россия http://thesz.livejournal.com
Дата: 07.01.09 05:16
Оценка:
BZ>>я часто сталкиваюсь с двумя точками зрения — первая "я знаком с ФП, но оно никакой выгоды в программировании не даёт", и вторая "я знаком с ФП, но так и не научился использовать его для повышения эффективности программирования". собственно, это одно и то же, разница только в том, на кого (ФП или самого человека) возлагается вина за отсутствие результата

S>Еще одно мнение в копилку — ФП дает выигрыш на узком классе задач, связанных со сложной работой с динамическими структурами данных (списками, деревьями, etc) — на что оно, собственно, и заточено.


Прошу прощения, но ФП ни под что конкретно не заточено.

Это всё в глазах наблюдателя.

S>А на том, что типично пишут программисты, оно особенно не нужно.


Какие программисты-то?
Yours truly, Serguey Zefirov (thesz NA mail TOCHKA ru)
Re[4]: Мастер-класс по ФП
От: thesz Россия http://thesz.livejournal.com
Дата: 07.01.09 05:19
Оценка:
Здравствуйте, subdmitry, Вы писали:

S>Здравствуйте, deniok, Вы писали:


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


S>Я считаю, что начинать надо с малого. Лично мне не очень понятно, как добиться на Хаскелле решения такой простой проблемы и (серьезно!) хочу его увидеть.


Что за проблема-то?

S>Оно не такое уж и бессмысленное. У меня при программировании часто возникает код, подобный написанному. Хочу понять, как получить его аналог в функциональных языках.


Лови:
doIt = do
    increment vA
    vB ! vA --> vD *= 10
    vF vG --> vH $= 0


Монады и операторы — твои друзья.

S>Некоторые тут, кстати, заявляют, что Хаскель лучший императивный язык...


Это не тут.

Это в Майкрософте.
Yours truly, Serguey Zefirov (thesz NA mail TOCHKA ru)
Re[4]: Мастер-класс по ФП
От: thesz Россия http://thesz.livejournal.com
Дата: 07.01.09 05:28
Оценка: 3 (1)
Y>>Пойми я тоже не хочу никого унизить, просто тут уже сразу видно невооруженным взглядом, отсутствие какой-либо базы, а без базы, тебе никто не сможет даже объяснить, почему ты не прав.

S>...решение этой задачи на Хаскелле. Особенно интересно в многопоточном варианте.


Приведенноё мной решение будет работать и с несколькими потоками тоже.

S>Если, конечно, наличествующая у вас база это позволяет сделать, а не просто утверждать, что "глобальные переменные это плохо".


Я это сделал.

Теперь жду от тебя аналогичного.

http://thesz.mskhug.ru/svn/hiersort/sim/Assembler.hs — код "ассемблера"
http://thesz.mskhug.ru/svn/hiersort/tests/matmul/MatMulProg.hs — пример использования
sendval ixlist inp v = do
    modindex <- nodemodindex node $ take 2 ixlist
    asend (oIndexes group fullixlist++oNode node) modindex arity (inp,v)
    where
        ixlen = length ixlist
        fullixlist
            | ixlen == 3 =
                sum ixlist:ixlist
                -- sum (take 2 ixlist):ixlist
                -- 0:ixlist
            | otherwise  = ixlist
        node = inpnode inp
        group = nodegroup node
        arity = inparity inp

nodeS dimn x y ix@[_,i,j,k] = do
    r <- fadd x y
    c <- ilt k dimn
    aif c
        (do
            k1 <- iadd k 1
            sendval [i,j,k1] SInputX r
        )
        (sendval [i,j] CInputX r)


Сделай-ка аналогичный "ассемблер" на твоём любимом ЯП. Чтобы не менее удобно было — чтобы можно было определять свои "подпрограммы" и тп.
Yours truly, Serguey Zefirov (thesz NA mail TOCHKA ru)
Re[7]: Мастер-класс по ФП
От: subdmitry Россия  
Дата: 07.01.09 05:37
Оценка:
Здравствуйте, VoidEx, Вы писали:

VE>Вот вам полный прикол:


Большое спасибо, это очень интересно.

То есть созданные в одной функции монадические переменные можно передавать в другие функции, но не делать глобальными. То есть для честного решения того примера в функцию DoIt() надо передать 7 переменных, чего народ ужаснулся. А так же передавать их во всю цепочку функций, из которых DoIt() вызывается. Меня сейчас будут пинать, но, признаться, в ряде случаев вариант с глобальными переменными мне нравится больше.

А можно описывать все функции в модуле как where-блок к блоку do в функции main, это не создат ли эффекта, что порожденные в блоке do переменные станут типа глобальными для этих функций?

VE>
VE>main = do
VE>    chan <- newChan :: IO ByteReads
VE>    s <- getChanContents chan
Насчет последней строчки. Она не лишняя? Или это очистка очереди от какого-то мусора?
VE>


VE>Кстати, я думал, что в Chan может быть один писатель, но не падает. Везёт, или писателей таки может быть куча?


Да вроде оно Control.Concurrent.Chan. По идее должно работать с несколькими потоками.
And if you listen very hard the alg will come to you at last.
Re[3]: Итог размышлнеий
От: FR  
Дата: 07.01.09 06:05
Оценка:
Здравствуйте, NotGonnaGetUs, Вы писали:

NGG>з.ы.

NGG>К дискуссии о "промышленном" решении можно будет вернуться, когда ФП окажется в массах и массы, пробежавшись по всем граблям n-раз, обобщат их до шаблонов, "запахов" и приниципов

Тот же Ерланг давно "промышленный".
Re[3]: Итог размышлнеий
От: FR  
Дата: 07.01.09 06:45
Оценка: 3 (2) +3
Здравствуйте, NotGonnaGetUs, Вы писали:

NGG>Посмотрел на нашу битву реализаций и решил-таки (благо новый год и есть свободное время) познакомиться с одним из языков формальных спецификаций (Z) поближе, что в итоге позволило взглянуть на поднятую тему под немного другим углом.


NGG>Прежде всего, предложенное обобщение слишком "обобщенное": для того, чтобы перейти к дизайну, а затем реализации, требуется принять ряд решений (наложить ограничения) от которых итоговое решение довольно сильно зависит. Эти ограничения не имеют никакой связи с языком программирования или "стилем" проектирования принятым в этом языке.


Я тоже смотрю на эту битву и недоумеваю, зачем вообще нужны такого рода обобщения.
Может дело в ява культуре, от которой я слишком далек, и подозреваю что из-за
относительной (даже к C++ и С#) бедности языка там без этого просто никуда.
Просто по моему опыту (C++) даже на довольно крупных проектах (сотни тысяч строк и
человеко-годы) такого рода обобщения практически бесполезны, когда нужна правка оказывается
что обобщенно не в ту сторону или проще заново написать пусть даже заплатку чем разбиратся
в слишком усложненом коде.

Ну и еще к разности культур, меня интересуют сейчас функциональные (а до этого динамические)
языки как инструмент для повышения производительности одиночки или небольшой группы.
Для этих целей такие языки очень хороши и по моему позволяют довольно сильно уменьшить
самую рутиную часть программирование — кодирование, так как по сути написание текста
на выразительных языках ближе к проектированию (или к конструированию)
чем к кодированию.
Re[3]: Мастер-класс по ФП
От: subdmitry Россия  
Дата: 07.01.09 06:50
Оценка:
Здравствуйте, thesz, Вы писали:

S>>Еще одно мнение в копилку — ФП дает выигрыш на узком классе задач, связанных со сложной работой с динамическими структурами данных (списками, деревьями, etc) — на что оно, собственно, и заточено.

T>Прошу прощения, но ФП ни под что конкретно не заточено.
T>Это всё в глазах наблюдателя.

Я под ФП понимаю направление человеческой мысли, разработавшей вполне конкретные языки (Lisp, ML, Haskell, Erlang, etc) со вполне конкретными фичами. Чисто функциями без side-эффектов можно писать и на С++, но его к ФП не причисляют, так как там этих фич и соответствующего синтаксического сахара нет (автоматическое управление паматью, patter matching, lambda functions, curring, high-order functions, etc). Реальная заточка этих фич — работа с рекурентными типами данных.

Естественно, в других языках есть и другие заточки, например, на работу с массивами.

Правда да, есть еще APL-J-K... Но это уже несколько другая область человеческой мысли, уж относят ли ее к ФП или нет.

S>>А на том, что типично пишут программисты, оно особенно не нужно.


T>Какие программисты-то?


Да самые разные. Вот, например, по-видимому самый широкий класс приложений — написание GUI к базам данных. Что-то не слышал, чтобы их вообще кто-то пытался писать на ФЯ. А почему? А потому что разработанные в ФЯ фичи для таких приложений малополезны.
And if you listen very hard the alg will come to you at last.
Re[5]: Мастер-класс по ФП
От: subdmitry Россия  
Дата: 07.01.09 06:52
Оценка:
Здравствуйте, thesz, Вы писали:

T>Лови:

T>
T>doIt = do
T>    increment vA
T>    vB ! vA --> vD *= 10
T>    vF vG --> vH $= 0
T>


T>Монады и операторы — твои друзья.


Еще нет, все никак не разберусь толком с монадами. Нельзя ли в паре слов описать, как эти vA, vB и т.д. попадают в функцию? Для простоты можно ограничиться только первой операцией.
And if you listen very hard the alg will come to you at last.
Re[5]: Мастер-класс по ФП
От: subdmitry Россия  
Дата: 07.01.09 07:09
Оценка:
Здравствуйте, thesz, Вы писали:

T>Сделай-ка аналогичный "ассемблер" на твоём любимом ЯП. Чтобы не менее удобно было — чтобы можно было определять свои "подпрограммы" и тп.


Могу предложить только интерпретатор Пролога, реализованный на Турбо Ассемблере, и то только в виде бинарника (сорцы утеряны). Смотреть в IDA.
http://eoda.by.ru/Data/CrackMes/aliencpu.rar
And if you listen very hard the alg will come to you at last.
Re[8]: Мастер-класс по ФП
От: VoidEx  
Дата: 07.01.09 07:16
Оценка:
Здравствуйте, subdmitry, Вы писали:


S>То есть для честного решения того примера в функцию DoIt() надо передать 7 переменных, чего народ ужаснулся. А так же передавать их во всю цепочку функций, из которых DoIt() вызывается. Меня сейчас будут пинать, но, признаться, в ряде случаев вариант с глобальными переменными мне нравится больше.

Если делать такие функции, то лучше императивный подход.
Прочтите ещё раз изначальную задачу, Вы решили (точнее предложили решение) её и так с кучей глобальных переменных, я их убрал. Возникает мысля, что если писать в ФП стиле, Вам просто не понадобится передавать по 7 переменных, не так ли?
Кстати, хочу посмотреть на это же на Вашем любимом языке, а то будет нечестно
Желательно императивным подходом (т.е. не мой код переписать на любой другой язык, а сделать так, как в этом языке обычно принято).

VE>>
VE>>main = do
VE>>    chan <- newChan :: IO ByteReads
VE>>    s <- getChanContents chan
S>Насчет последней строчки. Она не лишняя? Или это очистка очереди от какого-то мусора?
VE>>


Этой строкой я беру ленивый список всех попадающих в канал значений, который затем передаю в reader. Так же можно делать с файлами, например. reader, как можно заметить, понятия не имеет ни о каких Chan'ах, что даёт нам ещё одну степень абстракции. Я мог и reader'а-то не писать, а сразу написать, что мне со списком делать.
Re[7]: Мастер-класс по ФП
От: geniepro http://geniepro.livejournal.com/
Дата: 07.01.09 08:48
Оценка:
Здравствуйте, VoidEx, Вы писали:

VE>Кстати, я думал, что в Chan может быть один писатель, но не падает. Везёт, или писателей таки может быть куча?


В канал может писать кто угодно, в чей он области видимости, главное что бы тип записываемых данных подошёл. Фактически это же просто очередь с атомарным доступом (как у MVar)...
Re[4]: Мастер-класс по ФП
От: geniepro http://geniepro.livejournal.com/
Дата: 07.01.09 08:51
Оценка:
Здравствуйте, subdmitry, Вы писали:

S>Здравствуйте, deniok, Вы писали:


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


S>Я считаю, что начинать надо с малого. Лично мне не очень понятно, как добиться на Хаскелле решения такой простой проблемы и (серьезно!) хочу его увидеть.


S>Оно не такое уж и бессмысленное. У меня при программировании часто возникает код, подобный написанному. Хочу понять, как получить его аналог в функциональных языках.


А нельзя ли поподробнее? С описанием типов всех этих A, B, C, D...

S>Некоторые тут, кстати, заявляют, что Хаскель лучший императивный язык...


Уточнение: не лучший, а самый прекрасный в мире (Haskell is the world's finest imperative language)...
Re[6]: Мастер-класс по ФП
От: thesz Россия http://thesz.livejournal.com
Дата: 07.01.09 08:53
Оценка:
T>>
T>>vA = ...что-то там такое...
T>>increment var = do { x <- getV var; setV var (x+1) }
T>>doIt = do
T>>    increment vA
T>>    vB ! vA --> vD *= 10
T>>    vF vG --> vH $= 0
T>>

T>>Монады и операторы — твои друзья.
S>Еще нет, все никак не разберусь толком с монадами. Нельзя ли в паре слов описать, как эти vA, vB и т.д. попадают в функцию? Для простоты можно ограничиться только первой операцией.

vA — это название твоей A. В Хаскеле с большой буквы называются конструкторы данных.

То, что increment пришлось определять в отличии от встроенного ++, не беда совершенно.
Yours truly, Serguey Zefirov (thesz NA mail TOCHKA ru)
Re[8]: Мастер-класс по ФП
От: VoidEx  
Дата: 07.01.09 09:00
Оценка:
Здравствуйте, geniepro, Вы писали:

G>В канал может писать кто угодно, в чей он области видимости, главное что бы тип записываемых данных подошёл. Фактически это же просто очередь с атомарным доступом (как у MVar)...


Спасибо, просто у меня почему-то было ощущение, что это один писатель, много читателей. Не знаю, откуда взялось [ощущение].
Re[4]: Мастер-класс по ФП
От: thesz Россия http://thesz.livejournal.com
Дата: 07.01.09 09:16
Оценка:
S>>>Еще одно мнение в копилку — ФП дает выигрыш на узком классе задач, связанных со сложной работой с динамическими структурами данных (списками, деревьями, etc) — на что оно, собственно, и заточено.
T>>Прошу прощения, но ФП ни под что конкретно не заточено.
T>>Это всё в глазах наблюдателя.

S>Я под ФП понимаю направление человеческой мысли, разработавшей вполне конкретные языки (Lisp, ML, Haskell, Erlang, etc) со вполне конкретными фичами.


Теперь тебе осталось выяснить, как же понимает ФП весь остальной мир.

S>Чисто функциями без side-эффектов можно писать и на С++, но его к ФП не причисляют, так как там этих фич и соответствующего синтаксического сахара нет (автоматическое управление паматью, patter matching, lambda functions, curring, high-order functions, etc).


Вот в лиспе нет pattern matching и currying. Там даже tail call optimization нет. Он всё ещё ФЯ?

(с моей точки зрения, кстати, не так, чтобы особо

S> Реальная заточка этих фич — работа с рекурентными типами данных.


По-моему, здесь мы имеем дело с так называемым non-sequitur.

С логическим скачком, иными словами.

Не видно, как из перечисленных тобой свойств ФП следует заточенность под работу с рекурсивными типами данных.

S>Естественно, в других языках есть и другие заточки, например, на работу с массивами.


А в ФП нет? Почему? "Single Assignment C" does ring any bell?

S>>>А на том, что типично пишут программисты, оно особенно не нужно.


T>>Какие программисты-то?


S>Да самые разные. Вот, например, по-видимому самый широкий класс приложений — написание GUI к базам данных. Что-то не слышал, чтобы их вообще кто-то пытался писать на ФЯ.


По-моему, ты не слышал, как кто-либо вообще хоть что-нибудь писал на ФЯ.

S>А почему? А потому что разработанные в ФЯ фичи для таких приложений малополезны.


А с моей точки зрения потому, что уровень писателей таких приложений в своей массе весьма низок. Потому, что платят мало, потому, что средний наниматель таких программистов сам невысокого уровня.
Yours truly, Serguey Zefirov (thesz NA mail TOCHKA ru)
Re[6]: Мастер-класс по ФП
От: thesz Россия http://thesz.livejournal.com
Дата: 07.01.09 09:17
Оценка:
T>>Сделай-ка аналогичный "ассемблер" на твоём любимом ЯП. Чтобы не менее удобно было — чтобы можно было определять свои "подпрограммы" и тп.
S>Могу предложить только интерпретатор Пролога, реализованный на Турбо Ассемблере, и то только в виде бинарника (сорцы утеряны). Смотреть в IDA.
S>http://eoda.by.ru/Data/CrackMes/aliencpu.rar

Не уворачивайся. Давай исходник "ассемблера" на твоем ИЯП.

Потрудись немного, уважь старика Зефирова, всё равно сейчас делать нечего.
Yours truly, Serguey Zefirov (thesz NA mail TOCHKA ru)
Re[9]: Мастер-класс по ФП
От: geniepro http://geniepro.livejournal.com/
Дата: 07.01.09 09:58
Оценка: 18 (2)
Здравствуйте, VoidEx, Вы писали:

VE>Здравствуйте, geniepro, Вы писали:


G>>В канал может писать кто угодно, в чей он области видимости, главное что бы тип записываемых данных подошёл. Фактически это же просто очередь с атомарным доступом (как у MVar)...


VE>Спасибо, просто у меня почему-то было ощущение, что это один писатель, много читателей. Не знаю, откуда взялось [ощущение].


Я как-то баловался
Автор: geniepro
Дата: 14.05.07
-- сто тыщ потоков в один канал писать заставяля. А вот 300 тыщ потоков уже, блин, своппинг вызывают... Да, не Ерланг, всё-таки...

Кстати, с новым сборщиком мусора на этом тесте результаты чуть ли не на порядок лучше (на Pentium DualCore 1.6):
(опции +RTS -K100M -N2 -g2 -RTS )
10 100000 Time = 3.36
100 10000 Time = 3.80
1000 1000 Time = 5.55
10000 100 Time = 6.47
100000 10 Time = 7.42
250000  4 Time = 9.28

import IO
import Time
import System
import Control.Concurrent
import Control.Concurrent.Chan

data Ticker = Tick | Done

n_threads',          n_incs' :: Int
n_threads'  = 1000;  n_incs' = 1000

-- Командная строка: имя.exe +RTS -K100M -N2 -g2 -RTS кол-во_потоков кол-во_инкрементов

main = do
    arg <- getArgs
    let (n_threads, n_incs) = case (map read arg) of
            nt:ni:_ -> (nt,         ni)
            _       -> (n_threads', n_incs')

    t1  <- Time.getClockTime
    hSetBuffering stdout NoBuffering
    putStr $ "Config: " ++ show n_threads ++ " by " ++ show n_incs ++ "  Starting... "

    ticker <- newChan
    finish <- newChan
    result <- newChan

    let foo 0 = finish `writeChan` ()
        foo n = ticker `writeChan` Tick >> yield >> foo (n-1)

        makeThreads 0 = return ()
        makeThreads n = forkIO (foo n_incs) >> makeThreads (n-1)

        tick :: Int -> IO ()
        tick k = do msg <- readChan ticker
                    case msg of
                        Tick -> tick (k+1)
                        Done -> result `writeChan` k

        wait 0 = ticker `writeChan` Done
        wait n = readChan finish >> wait (n-1)

    forkIO     (tick 0)
    forkIO     (wait n_threads)
    makeThreads n_threads

    putStr "Finishing... "
    val <- readChan result
    putStr $ "Done: " ++ show val
    t2  <- Time.getClockTime; printTimeDif t2 t1

printTimeDif ft st = putStrLn $ " Time = " ++ show(Time.tdHour td) ++ ":" ++ show(Time.tdMin td) ++ ":"
                                           ++ show nsecs           ++ "." ++ snms
  where
    td           = ft `Time.diffClockTimes` st
    secs         = Time.tdSec     td
    ms           = Time.tdPicosec td `div` 1000000000
    (nsecs, nms) = if ms < 0 then (secs - 1, ms + 1000) else (secs, ms)
    snms         = reverse $ take 3 $ (reverse $ show nms) ++ "000"
Re[5]: Мастер-класс по ФП
От: geniepro http://geniepro.livejournal.com/
Дата: 07.01.09 10:04
Оценка:
Здравствуйте, thesz, Вы писали:

T>Вот в лиспе нет pattern matching и currying. Там даже tail call optimization нет. Он всё ещё ФЯ?


Ну вапще-то используя библиотеки, можно в лисп добавить и паттерн-матчинг, и карринг. А хвостовая рекурсия прямо указана в стандарте схемы, да и в CL тоже есть...

....

T>Потрудись немного, уважь старика Зефирова, всё равно сейчас делать нечего.


эх, а я вот и не заметил эти новогодние дни, потому как отдыхал всего пару дней...
Re[6]: Мастер-класс по ФП
От: thesz Россия http://thesz.livejournal.com
Дата: 07.01.09 18:13
Оценка:
T>>Вот в лиспе нет pattern matching и currying. Там даже tail call optimization нет. Он всё ещё ФЯ?
G>Ну вапще-то используя библиотеки, можно в лисп добавить и паттерн-матчинг, и карринг. А хвостовая рекурсия прямо указана в стандарте схемы, да и в CL тоже есть...

"Я так и думал." (C) Администратор из Half-Life 2.

Yours truly, Serguey Zefirov (thesz NA mail TOCHKA ru)
Re[5]: Мастер-класс по ФП
От: Mr.Cat  
Дата: 07.01.09 19:59
Оценка:
Здравствуйте, VoidEx, Вы писали:
VE>Я бы советовал начинать сразу с throw или longjmp

Осмысленные (т.е. механизм исключений, например, а не longjmp(random())) нелокальные переходы в принципе не очень сложно сделать через континуации.
Re[7]: Мастер-класс по ФП
От: subdmitry Россия  
Дата: 07.01.09 20:18
Оценка:
Здравствуйте, thesz, Вы писали:

T>>>vA = ...что-то там такое...


Ага, так вот они, глобальные переменные в Хаскелле! Все, понял. Действительно, императивные фичи можно реализовать, что радует.
And if you listen very hard the alg will come to you at last.
Re[5]: Мастер-класс по ФП
От: Mr.Cat  
Дата: 07.01.09 20:26
Оценка: +1
Здравствуйте, thesz, Вы писали:

T>Вот в лиспе

Каком?

T>нет pattern matching

Могу про scheme сказать. В макросах матчинг есть по стандарту. Для матчинга в "рантаймовом коде" есть пара реализаций на макросах (с минимальными различиями в интефейсе), которые в нормальных имплементациях схемы доступны из коробки. Так что можно считать, что в схеме есть матчинг.

T>и currying.

Если очень хочется — можно на макросах сделать. Конечно, будет не так красиво, как в хаскеле, но частичное применение в любом случае не стыкуется красиво с переменным числом аргументов.

Там даже tail call optimization нет.
В схеме есть. Более того, там все вызовы в итоге получаются хвостовыми.
Re[5]: Мастер-класс по ФП
От: subdmitry Россия  
Дата: 07.01.09 20:37
Оценка:
Здравствуйте, thesz, Вы писали:

S>>Чисто функциями без side-эффектов можно писать и на С++, но его к ФП не причисляют, так как там этих фич и соответствующего синтаксического сахара нет (автоматическое управление паматью, patter matching, lambda functions, curring, high-order functions, etc).

T>Вот в лиспе нет pattern matching и currying. Там даже tail call optimization нет. Он всё ещё ФЯ?

В мире приято считать, что да.

В общем-то я не имел в виду, что этот список обязательно должен весь присутствовать в языке, чтобы его считать функциональным.

S>> Реальная заточка этих фич — работа с рекурентными типами данных.

T>Не видно, как из перечисленных тобой свойств ФП следует заточенность под работу с рекурсивными типами данных.

Ну по крайней мере первые две это явно оно. А остальные... часто они у тебя используются применительно к нединамическим типам данных?

S>>Естественно, в других языках есть и другие заточки, например, на работу с массивами.

T>А в ФП нет? Почему? "Single Assignment C" does ring any bell?

Синтаксическая поддержка может и есть. Другой вопрос, насколько оно эффективно компилируется.

Хотя в том же ML код для массивов наверное неплохой.

S>>А почему? А потому что разработанные в ФЯ фичи для таких приложений малополезны.


T>А с моей точки зрения потому, что уровень писателей таких приложений в своей массе весьма низок. Потому, что платят мало, потому, что средний наниматель таких программистов сам невысокого уровня.


Языки и средства разработки изобретают совсем не те люди, которые ими un mass пользуются. И у первых уровень отнюдь не низок. Если бы они чувствовали, что ФП дает, ну скажем, 50% выигрыша по времени разработки, его бы уже давно совали бы во все RADы. Но нет, реальность такова, что туда суют даже такие недоязыки как Паскаль или Бэйсик (языки без обобщенного программирования, брр), и ничего, для решения типичных программистских задач они оказываются вполне даже ничего.
And if you listen very hard the alg will come to you at last.
Re: Мастер-класс по ФП
От: Mr.Cat  
Дата: 07.01.09 20:38
Оценка:
Здравствуйте, BulatZiganshin, Вы писали:
BZ>я предлагаю начинать с попыток "учеников" самим решить предлагаемые задачи, и затем демонстрации "учителями" их решений

А продолжать будем? А то что-то дискуссия уехала куда-то.
Re[7]: Мастер-класс по ФП
От: subdmitry Россия  
Дата: 07.01.09 20:45
Оценка:
Здравствуйте, thesz, Вы писали:

S>>Могу предложить только интерпретатор Пролога, реализованный на Турбо Ассемблере, и то только в виде бинарника (сорцы утеряны).


T>Не уворачивайся. Давай исходник "ассемблера" на твоем ИЯП.


Зря ты так, интерпретатор Пролога намного интереснее. 300 байт интерпретатора + 300 байт кода к нему. Один человек смог сломать, у него ушло 3 недели.

T>Потрудись немного, уважь старика Зефирова, всё равно сейчас делать нечего.


Я что-то не очень понимаю проблематику, зачем на ЯВУ реализовывать ассемблер, обычно делают наоборот. Ну так, навскидку

#define add(a,b) (a)+=(b);

И дальше в таком духе. Не подходит?
And if you listen very hard the alg will come to you at last.
Re[6]: Мастер-класс по ФП
От: Mr.Cat  
Дата: 07.01.09 20:51
Оценка:
Здравствуйте, subdmitry, Вы писали:

S>>>А почему? А потому что разработанные в ФЯ фичи для таких приложений малополезны.

На мой взгляд, весь вопрос в архитектуре приложения. Если писать код изначально в стиле C[whatever] — и потом пытаться перевести его напрямую на хаскель (ML, лисп и т.п.) — ничего путного не выйдет (равно как и в обратную сторону). Поэтому вопрос не в том, как в хаскеле работать со, скажем, глобальным состоянием, а как реализовывать на хаскеле те задачи, для который в C[whatever] используется глобальное состояние. Например, есть концепция реактивного (reactive), которая позволяет отказаться от глобального состояния в программах, ориентированных на GUI (сам я с этим всем толком не знаком — просто предлагаю ознакомиться ради интереса).

S>Языки и средства разработки изобретают совсем не те люди, которые ими un mass пользуются. И у первых уровень отнюдь не низок.

Есть мнение, что низок уровень вторых. Ничего категоричного утверждать не хочу, ибо сам на C# на жизнь зарабатываю
Re[10]: Мастер-класс по ФП
От: thesz Россия http://thesz.livejournal.com
Дата: 07.01.09 21:25
Оценка:
VE>>Спасибо, просто у меня почему-то было ощущение, что это один писатель, много читателей. Не знаю, откуда взялось [ощущение].
G>Я как-то баловался
Автор: geniepro
Дата: 14.05.07
-- сто тыщ потоков в один канал писать заставяля. А вот 300 тыщ потоков уже, блин, своппинг вызывают... Да, не Ерланг, всё-таки...


А у Эрланга таких проблем нет?

То есть, он не свопится, когда у нас есть 300 тысяч потоков? (каждый по (327+233)*word_size, то есть, 1.2К)

(кстати, нарыл интересное
Yours truly, Serguey Zefirov (thesz NA mail TOCHKA ru)
Re[8]: Мастер-класс по ФП
От: thesz Россия http://thesz.livejournal.com
Дата: 07.01.09 21:26
Оценка:
S>Я что-то не очень понимаю проблематику, зачем на ЯВУ реализовывать ассемблер, обычно делают наоборот. Ну так, навскидку
S>#define add(a,b) (a)+=(b);
S>И дальше в таком духе. Не подходит?

Не совсем.

Ты изучи проблему-то, изучи.
Yours truly, Serguey Zefirov (thesz NA mail TOCHKA ru)
Re[6]: Мастер-класс по ФП
От: thesz Россия http://thesz.livejournal.com
Дата: 07.01.09 21:29
Оценка:
T>>Вот в лиспе
MC>Каком?

Стандартном.

T>>нет pattern matching

MC>Могу про scheme сказать.

Схема — не лисп.

К схеме у меня уважения больше, прошу прощения.

Они хоть не тянули 25 лет, перед тем, как придать нормальную семантику вычислениям функций.
Yours truly, Serguey Zefirov (thesz NA mail TOCHKA ru)
Re[11]: Мастер-класс по ФП
От: Курилка Россия http://kirya.narod.ru/
Дата: 07.01.09 21:31
Оценка:
Здравствуйте, thesz, Вы писали:

VE>>>Спасибо, просто у меня почему-то было ощущение, что это один писатель, много читателей. Не знаю, откуда взялось [ощущение].

G>>Я как-то баловался
Автор: geniepro
Дата: 14.05.07
-- сто тыщ потоков в один канал писать заставяля. А вот 300 тыщ потоков уже, блин, своппинг вызывают... Да, не Ерланг, всё-таки...


T>А у Эрланга таких проблем нет?


T>То есть, он не свопится, когда у нас есть 300 тысяч потоков? (каждый по (327+233)*word_size, то есть, 1.2К)


Откуда ты плюс взял? В источнике вроде:

327 words when spawned including a heap of 233 words.

Да и даже 1.2к * 300000 = 360 М, вроде не так чтоб много
Re[6]: Мастер-класс по ФП
От: thesz Россия http://thesz.livejournal.com
Дата: 07.01.09 21:35
Оценка:
S>>> Реальная заточка этих фич — работа с рекурентными типами данных.
T>>Не видно, как из перечисленных тобой свойств ФП следует заточенность под работу с рекурсивными типами данных.
S>Ну по крайней мере первые две это явно оно. А остальные... часто они у тебя используются применительно к нединамическим типам данных?

Это лямбды, карринг, ФВП.

Они у меня используются часто. Только я не понимаю, что такое "нединамические типы данных".

S>>>Естественно, в других языках есть и другие заточки, например, на работу с массивами.

T>>А в ФП нет? Почему? "Single Assignment C" does ring any bell?
S>Синтаксическая поддержка может и есть. Другой вопрос, насколько оно эффективно компилируется.
S>Хотя в том же ML код для массивов наверное неплохой.

SAC бьёт Фортран. Наверное, они неплохо компилируют.

T>>А с моей точки зрения потому, что уровень писателей таких приложений в своей массе весьма низок. Потому, что платят мало, потому, что средний наниматель таких программистов сам невысокого уровня.

S>Языки и средства разработки изобретают совсем не те люди, которые ими un mass пользуются. И у первых уровень отнюдь не низок.

Ты мне про компиляторщиков не говори. Я с ними близко общался на протяжении полутора лет.

— Это надо писать на плюсах, так удобней, чем на Си.
— Но это же можно и на ФЯ, например, на Хаскеле!
— На Хаскеле никто не пишет, все пишут на Си или Си++. И вообще, не мешай работать, ты компиляторы никогда не писал и ничего про их написание не знаешь.

Дословный диалог, буквально.

Так что ты мне про уровень не говори, повторюсь. Я этот уровень лбом ощутил.
Yours truly, Serguey Zefirov (thesz NA mail TOCHKA ru)
Re[6]: Мастер-класс по ФП
От: Beam Россия  
Дата: 07.01.09 21:53
Оценка: +1
Здравствуйте, Mr.Cat, Вы писали:

T>>и currying.

MC>Если очень хочется — можно на макросах сделать. Конечно, будет не так красиво, как в хаскеле, но частичное применение в любом случае не стыкуется красиво с переменным числом аргументов.

Так есть же. SRFI 26

(cut cons (+ a 1) <>) is the same as (lambda (x2) (cons (+ a 1) x2))
(cut list 1 <> 3 <> 5) is the same as (lambda (x2 x4) (list 1 x2 3 x4 5))
(cut list) is the same as (lambda () (list))
(cut list 1 <> 3 <...>) is the same as (lambda (x2 . xs) (apply list 1 x2 3 xs))
(cut <> a b) is the same as (lambda (f) (f a b))


И с переменным числом аргументов выглядит не так уж и плохо.

Кстати, почему в Haskell не сделают что-то типа
map _ [1,2,3] as \f -> map f [1,2,3]
Ну т.е. аналогично scheme.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Best regards, Буравчик
Re[7]: Мастер-класс по ФП
От: deniok Россия  
Дата: 07.01.09 22:05
Оценка:
Здравствуйте, Beam, Вы писали:


B>Кстати, почему в Haskell не сделают что-то типа

B>map _ [1,2,3] as \f -> map f [1,2,3]
B>Ну т.е. аналогично scheme.

А зачем, если можно с той же целью написать
flip map [1,2,3]
Re[7]: Мастер-класс по ФП
От: Mr.Cat  
Дата: 07.01.09 22:12
Оценка: +1
Здравствуйте, thesz, Вы писали:
Все, все, понял, Вы про CL. Мне он, кстати, тоже как-то не понравился.

T>Схема — не лисп.

Простите уж мое занудство.

Scheme is a statically scoped and properly tail-recursive dialect of the Lisp programming language invented by Guy Lewis Steele Jr. and Gerald Jay Sussman.

(с) RnRS.
Re[7]: Мастер-класс по ФП
От: Mr.Cat  
Дата: 07.01.09 22:15
Оценка:
Здравствуйте, Beam, Вы писали:
B>Так есть же. SRFI 26

Кстати, да, спасибо, что напомнили.
Re[8]: Мастер-класс по ФП
От: Beam Россия  
Дата: 07.01.09 22:21
Оценка:
Здравствуйте, deniok, Вы писали:

B>>Кстати, почему в Haskell не сделают что-то типа

B>>map _ [1,2,3] as \f -> map f [1,2,3]
B>>Ну т.е. аналогично scheme.

D>А зачем, если можно с той же целью написать

D>
D>flip map [1,2,3]
D>


Я знал, что ответ будет таким Ну да ладно...

Затем, что
1. flip map [1,2,3] труднее воспринять чем map _ [1,2,3] (я понимаю, что это спорно)
2. существует функции у которых количество параметров больше двух
3. можно было бы работать с tuple: fun (_,1) _ ===> \x y -> fun (x,1) y
4. и может даже так: putStrLn $ "A=" ++ (show _) ++ "B=" ++ (show _) ===> \a b -> putStrLn $ "A=" ++ (show a) ++ "B=" ++ (show b)
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Best regards, Буравчик
Re[9]: Мастер-класс по ФП
От: Mr.Cat  
Дата: 07.01.09 22:46
Оценка: +1
Здравствуйте, Beam

На мой взгляд, пункты 3 и 4 ломают семантику языка.

B>3. можно было бы работать с tuple: fun (_,1) _ ===> \x y -> fun (x,1) y

B>4. и может даже так: putStrLn $ "A=" ++ (show _) ++ "B=" ++ (show _) ===> \a b -> putStrLn $ "A=" ++ (show a) ++ "B=" ++ (show b)

Как видно, андерскор вне паттерна превращается в жуткую волшебную палочку, т.е. (_, 1) — больше не тупл, а fun (_, 1) — не применение функции fun к выражению (_, 1). Я бы не хотел, чтобы в хаскеле появилось что-то подобное.

Кстати, я так и не удосужился познакомиться с Template Haskell — с его помощью разве нельзя сделать частичное применение, как в srfi-26?
Re[9]: Мастер-класс по ФП
От: deniok Россия  
Дата: 07.01.09 22:57
Оценка:
Здравствуйте, Beam, Вы писали:

B>Я знал, что ответ будет таким Ну да ладно...


B>Затем, что

B>1. flip map [1,2,3] труднее воспринять чем map _ [1,2,3] (я понимаю, что это спорно)
map `flip` [1,2,3]

или (для любителей явных операторов) приводим к тому, что просили
(***) = flip
map *** [1,2,3]


B>2. существует функции у которых количество параметров больше двух

А тут отложенное неименованное связывание легко рушит семантику

B>3. можно было бы работать с tuple: fun (_,1) _ ===> \x y -> fun (x,1) y

Да-да, вот тут и рушит Представь, что fun это flip и вспомни что вычисления идут лениво слева направо...
Re[10]: Мастер-класс по ФП
От: Beam Россия  
Дата: 07.01.09 23:00
Оценка:
Здравствуйте, Mr.Cat, Вы писали:

MC>Здравствуйте, Beam


MC>На мой взгляд, пункты 3 и 4 ломают семантику языка.


B>>3. можно было бы работать с tuple: fun (_,1) _ ===> \x y -> fun (x,1) y

B>>4. и может даже так: putStrLn $ "A=" ++ (show _) ++ "B=" ++ (show _) ===> \a b -> putStrLn $ "A=" ++ (show a) ++ "B=" ++ (show b)

MC>Как видно, андерскор вне паттерна превращается в жуткую волшебную палочку, т.е. (_, 1) — больше не тупл, а fun (_, 1) — не применение функции fun к выражению (_, 1). Я бы не хотел, чтобы в хаскеле появилось что-то подобное.


Ну я и не настаивал, спросил почему не делают. В принципе, не обязательно использовать подчеркивание.
А вообще, я согласен, что это усложнит язык. Проще и понятнее написать лишнюю функцию в where.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Best regards, Буравчик
Re[10]: Мастер-класс по ФП
От: Beam Россия  
Дата: 07.01.09 23:09
Оценка:
ОК. Убедили, что так делать не надо

Здравствуйте, deniok, Вы писали:

D>
D>map `flip` [1,2,3]
D>

D>или (для любителей явных операторов) приводим к тому, что просили
D>
D>(***) = flip
D>map *** [1,2,3]
D>


Оригинально.

B>>2. существует функции у которых количество параметров больше двух

D>А тут отложенное неименованное связывание легко рушит семантику

B>>3. можно было бы работать с tuple: fun (_,1) _ ===> \x y -> fun (x,1) y

D>Да-да, вот тут и рушит Представь, что fun это flip и вспомни что вычисления идут лениво слева направо...

Вот здесь не понял. Ведь это ничем не отличается от
let myFun x y = fun (x,1) y in myFun 1 2
И вроде, здесь нет проблем никаких
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Best regards, Буравчик
Re[12]: Мастер-класс по ФП
От: thesz Россия http://thesz.livejournal.com
Дата: 07.01.09 23:12
Оценка:
T>>То есть, он не свопится, когда у нас есть 300 тысяч потоков? (каждый по (327+233)*word_size, то есть, 1.2К)

К>Откуда ты плюс взял?


Просмотрел. Но я нашёл поинтересней.

K>В источнике вроде:

К>

К>327 words when spawned including a heap of 233 words.

К>Да и даже 1.2к * 300000 = 360 М, вроде не так чтоб много

The default suggested stack size is 16 kilowords

Оба-на!
Yours truly, Serguey Zefirov (thesz NA mail TOCHKA ru)
Re[8]: Мастер-класс по ФП
От: thesz Россия http://thesz.livejournal.com
Дата: 07.01.09 23:15
Оценка:
T>>>>vA = ...что-то там такое...
S>Ага, так вот они, глобальные переменные в Хаскелле! Все, понял. Действительно, императивные фичи можно реализовать, что радует.

Ну, это уж ты как-то совсем того.

Есть такой сайт, Lambda the Ultimate, назван по серии статей Гая Стила (Guy Steele), одного из разработчиков Java.

В этой серии одна из статей называется "Lambda the Ultimate Imperative". AFAIK, там рассказывается про императивное программирование с точки зрения ЛИ.

Могу ошибаться, правда.
Yours truly, Serguey Zefirov (thesz NA mail TOCHKA ru)
Re[11]: Мастер-класс по ФП
От: deniok Россия  
Дата: 07.01.09 23:25
Оценка:
Здравствуйте, Beam, Вы писали:



B>>>3. можно было бы работать с tuple: fun (_,1) _ ===> \x y -> fun (x,1) y

D>>Да-да, вот тут и рушит Представь, что fun это flip и вспомни что вычисления идут лениво слева направо...

B>Вот здесь не понял. Ведь это ничем не отличается от

B>let myFun x y = fun (x,1) y in myFun 1 2
B>И вроде, здесь нет проблем никаких

У тебя два неименованных места для будущего связывания
myComb = fun (_,1) _

Если потом вызвать
myComb x y

то связывание просто по месту не пройдет, нужны имена (обычная лямбда) или номера (индексы де Бр(ой)(ау)на). Поскольку если fun у тебя, например, такой
fun (a,b) c = c a

то вычисление пойдет так
myComb x y ~> 
(fun (_,1) _)  x y ~>
(_ _) x y ~>
x y
 -- ой мама биндеры переставились, должно-то быть y x
Re[5]: Мастер-класс по ФП
От: FR  
Дата: 08.01.09 04:18
Оценка:
Здравствуйте, thesz, Вы писали:

T>Сделай-ка аналогичный "ассемблер" на твоём любимом ЯП. Чтобы не менее удобно было — чтобы можно было определять свои "подпрограммы" и тп.


Если любимый язык forth то легко

http://netlib.narod.ru/library/book0001/ch03_11.htm
Re[8]: Мастер-класс по ФП
От: geniepro http://geniepro.livejournal.com/
Дата: 08.01.09 05:26
Оценка: 4 (1)
Здравствуйте, deniok, Вы писали:

D>Здравствуйте, Beam, Вы писали:



B>>Кстати, почему в Haskell не сделают что-то типа

B>>map _ [1,2,3] as \f -> map f [1,2,3]
B>>Ну т.е. аналогично scheme.

D>А зачем, если можно с той же целью написать

D>
D>flip map [1,2,3]
D>

или так:
(`map` [1,2,3])
Re[7]: Мастер-класс по ФП
От: BulatZiganshin  
Дата: 08.01.09 09:13
Оценка:
Здравствуйте, Beam, Вы писали:

B>Кстати, почему в Haskell не сделают что-то типа

B>map _ [1,2,3] as \f -> map f [1,2,3]

я слышал, что проблема в том, что непонятно, где вставлять \_. например в твоём примере:
map (\f -> f) [1,2,3]
или
\f -> map f [1,2,3]
или
(\f -> map f) [1,2,3]
Люди, я люблю вас! Будьте бдительны!!!
Re: Мастер-класс по ФП
От: Tonal- Россия www.promsoft.ru
Дата: 08.01.09 10:53
Оценка:
Здравствуйте, BulatZiganshin, Вы писали:
Просили продолжения
Автор: Mr.Cat
Дата: 07.01.09
, как обществу такая задачка:
Дано:
Дерево в текстовом файле. Новый уровень +2 пробела, типа такого:
Животные
  рыбы
    щука
    карась
  птицы
    перепел
Растения
  ...

Нужно:
1. Зачитать это в
data Tree = Leaf Int String | Tree Int String [Tree]

где Int — номер строки, String — строка без начатльных и конечных пробелов
2. Сделать чтение устойчивым к ошибкам пропуска уровня типа:
Животные
    карась
  птицы
    перепел
Растения
  ...

3. Написать код печати дерева в исходном виде
4. Отфильтровать дерево, оставив только ветви содержащие дубликаты и сами дубликаты.
... << RSDN@Home 1.2.0 alpha 4 rev. 0>>
Re[13]: Мастер-класс по ФП
От: Курилка Россия http://kirya.narod.ru/
Дата: 08.01.09 12:12
Оценка:
Здравствуйте, thesz, Вы писали:

T>>>То есть, он не свопится, когда у нас есть 300 тысяч потоков? (каждый по (327+233)*word_size, то есть, 1.2К)


К>>Откуда ты плюс взял?


T>Просмотрел. Но я нашёл поинтересней.


[cut]

T>The default suggested stack size is 16 kilowords


T>Оба-на!


Ммм, Сергей, думаю стоит внимательней читать, ну какое отношение стэк эмулятора имеет к памяти тех процессов, которые в нём крутятся?
И чуть ниже смотрим:

+h Size
Sets the default heap size of processes to the size Size.

Вот этот другой (а не +a size) параметр задаёт размер хипов процессов.
Re[2]: Мастер-класс по ФП
От: BulatZiganshin  
Дата: 08.01.09 13:38
Оценка: 4 (1)
Здравствуйте, Tonal-, Вы писали:

T>1. Зачитать это в

T>
T>data Tree = Leaf Int String | Tree Int String [Tree]
T>

T>где Int — номер строки, String — строка без начатльных и конечных пробелов

во-первых, проще
data Tree = Tree Int String [Tree]


во-вторых, в постановке задачи ошибка попробуй сам описать, какое дерево должно быть построено по твоим входным данным

task1 =  zip [1..]  -- нумеруем строки
     >>> makeTrees  -- формируем список деревьев

makeTrees =  groupFrom (not.isSpace.head.snd)  -- разбиваем на группы, начинающиеся со строки без отступа
                 -- в каждой из групп первую строчку используем для заголовка дерева
         >>> map ( \((linenum,string):xs) -> Tree linenum string  
                                                  -- а в оставшихся строках отрезаем первые два пробела и 
                                                  -- рекурсивно изготовляем из них список деревьев
                                                  (makeTrees (map (drop 2) xs)))



T>2. Сделать чтение устойчивым к ошибкам пропуска уровня типа:

T>
T>Животные
T>    карась
T>  птицы
T>    перепел
T>Растения
T>  ...
T>


не совсем понятно, что значит сделать устойчивым? на мой взгляд, нужно просто подавать жалобу из groupFrom:

groupFrom crit [] = []
groupFrom crit (x:xs) | not (crit x) = error "ах вы суки эдакие!!!"
groupFrom crit (x:xs) = let (l1:l2) = break crit xs
                        in (x:l1) : groupFrom crit l2



T>3. Написать код печати дерева в исходном виде


print (Tree linenum string subtrees) = string : map ("  "++) (concatMap print subtrees)



T>4. Отфильтровать дерево, оставив только ветви содержащие дубликаты и сами дубликаты.


-- составление списка строк в дереве
strings (Tree _ string subtrees) = string : concatMap strings subtrees

-- получение дубликатов из списка строк
dups = sort >>> group >>> filter (not.null.tail) >>> map head

-- Оставить только часть дерева, содержащую строки из списка strList
onlyFrom strList (Tree linenum string subtrees) = 
  case (string `elem` strList, mapCatMaybes onlyDups subtrees) of
    (False, []) -> Nothing
    (_,     xs) -> Just (Tree linenum string xs)

task4 tree = onlyFrom (dups (strings tree)) tree
Люди, я люблю вас! Будьте бдительны!!!
Re[3]: Мастер-класс по ФП
От: Tonal- Россия www.promsoft.ru
Дата: 08.01.09 16:30
Оценка:
Здравствуйте, BulatZiganshin, Вы писали:

Действительно "мастер-класс"!
У меня кода получилось в несколько раз больше.

BZ>не совсем понятно, что значит сделать устойчивым? на мой взгляд, нужно просто подавать жалобу из groupFrom:

BZ>
BZ>groupFrom crit [] = []
BZ>groupFrom crit (x:xs) | not (crit x) = error "ах вы суки эдакие!!!"
BZ>groupFrom crit (x:xs) = let (l1:l2) = break crit xs
BZ>                        in (x:l1) : groupFrom crit l2
BZ>

Я имел в виду, что дерево нужно строить дальше, хотя кончно так и нужно было написать в постановке.
Хотя groupFrom довольно просто изменить под это:
groupFrom crit [] = []
groupFrom crit (x:xs) = 
  let 
    (l1:l2) = break crit xs
    grp x l | not (crit x) = (fst x, "(!)") : x : l
    grp x l = x:l
  in grp x l1 : groupFrom crit l2
... << RSDN@Home 1.2.0 alpha 4 rev. 0>>
Re[14]: Мастер-класс по ФП
От: thesz Россия http://thesz.livejournal.com
Дата: 08.01.09 17:04
Оценка:
T>>The default suggested stack size is 16 kilowords
T>>Оба-на!
К>Ммм, Сергей, думаю стоит внимательней читать, ну какое отношение стэк эмулятора имеет к памяти тех процессов, которые в нём крутятся?

А стек самого процесса, который крутится, где задаётся?

Как я понимаю, он у каждого "легкого процесса" должен быть свой.

К>И чуть ниже смотрим:

К>

К>+h Size
К> Sets the default heap size of processes to the size Size.

К>Вот этот другой (а не +a size) параметр задаёт размер хипов процессов.

Я про стек начал говорить. Стек для каждого из процессов, наверное, в хип не входит.
Yours truly, Serguey Zefirov (thesz NA mail TOCHKA ru)
Re[15]: Мастер-класс по ФП
От: Курилка Россия http://kirya.narod.ru/
Дата: 08.01.09 17:18
Оценка:
Здравствуйте, thesz, Вы писали:

T>>>The default suggested stack size is 16 kilowords

T>>>Оба-на!
К>>Ммм, Сергей, думаю стоит внимательней читать, ну какое отношение стэк эмулятора имеет к памяти тех процессов, которые в нём крутятся?

T>А стек самого процесса, который крутится, где задаётся?


T>Как я понимаю, он у каждого "легкого процесса" должен быть свой.


А фиг знет где конкретно, тупо просмотром через запуск процесса и вызов process_info/1 выдаёт 8 слов. Но сдаётся мне, что они как раз в те предыдущие 327 как раз входят.
Re[3]: Мастер-класс по ФП
От: Tonal- Россия www.promsoft.ru
Дата: 08.01.09 17:57
Оценка:
Здравствуйте, BulatZiganshin, Вы писали:
T>>3. Написать код печати дерева в исходном виде
BZ>
BZ>print (Tree linenum string subtrees) = string : map ("  "++) (concatMap print subtrees)
BZ>

А как можно изменить этот код чтобы в в начале каждой строки отображался linenum нода?

T>>4. Отфильтровать дерево, оставив только ветви содержащие дубликаты и сами дубликаты.

И опять я не точно выразился.
Дубликатами считаются одинаковые строки одного родителя.
Например:
Животные
  рыбы
    щука
    карась
    щука

После фильтрации должно получится:
Животные
  рыбы
    щука
    щука

Как подправить, сходу не соображу.

Кстати, в моей инсталяции ghc 6.10.1 для винды, в документации нет упоминания mapCatMaybes и описания модуля Maybes тоже нет. Зато есть пустая страница для модуля Maybe
... << RSDN@Home 1.2.0 alpha 4 rev. 0>>
Re[16]: Мастер-класс по ФП
От: thesz Россия http://thesz.livejournal.com
Дата: 08.01.09 18:09
Оценка:
T>>А стек самого процесса, который крутится, где задаётся?
T>>Как я понимаю, он у каждого "легкого процесса" должен быть свой.
К>А фиг знет где конкретно, тупо просмотром через запуск процесса и вызов process_info/1 выдаёт 8 слов. Но сдаётся мне, что они как раз в те предыдущие 327 как раз входят.

Короче говоря, ничего не понятно.

Ты бы провёл эксперимент с 300K отправителей.
Yours truly, Serguey Zefirov (thesz NA mail TOCHKA ru)
Re[4]: Мастер-класс по ФП
От: Beam Россия  
Дата: 08.01.09 18:46
Оценка:
Здравствуйте, Tonal-, Вы писали:

T>Кстати, в моей инсталяции ghc 6.10.1 для винды, в документации нет упоминания mapCatMaybes и описания модуля Maybes тоже нет. Зато есть пустая страница для модуля Maybe


По-моему функции mapCatMaybes в стандартной библиотеке нет сейчас и не было раньше.
А Maybe перенесли в Data.Maybe. Но модуль Maybe тоже будет работать, так как просто импортирует Data.Maybe
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Best regards, Буравчик
Re[4]: Мастер-класс по ФП
От: BulatZiganshin  
Дата: 08.01.09 19:04
Оценка:
Здравствуйте, Tonal-, Вы писали:

T>А как можно изменить этот код чтобы в в начале каждой строки отображался linenum нода?


show?

T>>>4. Отфильтровать дерево, оставив только ветви содержащие дубликаты и сами дубликаты.

T>Как подправить, сходу не соображу.

строй список дубликатов локально

T>Кстати, в моей инсталяции ghc 6.10.1 для винды, в документации нет упоминания mapCatMaybes и описания модуля Maybes тоже нет. Зато есть пустая страница для модуля Maybe


это из модуля Data.Maybe, я просто держу на диске исходники
Люди, я люблю вас! Будьте бдительны!!!
Re[2]: Мастер-класс по ФП
От: Гест Украина https://zverok.github.io
Дата: 08.01.09 20:29
Оценка:
Здравствуйте, Tonal-, Вы писали:

T>Здравствуйте, BulatZiganshin, Вы писали:

T>Просили продолжения
Автор: Mr.Cat
Дата: 07.01.09
, как обществу такая задачка:


Line = Struct.new(:string, :num, :level)
Leaf = Struct.new(:string, :num, :children)

def read_children(lines, level = 0)
    return [], [] if lines.empty?
    first, *rest = *lines
    case 
        when first.level == level
            children, rest = read_children(rest, level+1)
            first_child = Leaf[first.string, first.num, children]
            sibling, rest = read_children(rest, level)
            return [first_child] + sibling, rest
        when first.level < level
            return [], lines
        when first.level > level
            raise(RuntimeError, "wrong level at line #{first.num}")
    end
end

def print_tree(leafs, level = 0)
    leafs.each do |leaf|
        puts "   " * level + leaf.string
        print_tree(leaf.children, level + 1)
    end
end

def select_duplicates(leafs)
    dup_strings = leafs.
        map{|leaf| leaf.string}.
        group_by{|str| str}.
        select{|str, group| group.size > 1}.
        map{|str, group| str}

    leafs.
        map{|leaf| Leaf[leaf.string, leaf.num, select_duplicates(leaf.children)]}.
        select{|leaf| dup_strings.include?(leaf.string) || !leaf.children.empty?}
end

lines = File.read('tree.txt').split("\n").
    to_enum(:each_with_index).
    map{|ln, idx| Line[ln.strip, idx, ln[/^\s*/].length/2]}

roots, * = read_children(lines)
print_tree(roots)
dups = select_duplicates(roots)
print_tree(dups)
Re[4]: Подумалось...
От: NotGonnaGetUs  
Дата: 09.01.09 13:24
Оценка: 4 (2)
Здравствуйте, Beam, Вы писали:

B>
B>-- универсальная функция, группирует список пар (ключ, значение) по ключу, возвращая список пар (ключ, [значения для этого ключа])
B>-- например groupByFst [(1,2), (1,3), (2,5), (1,4), (2,6)] возвращает [ (1,[2,3,4]), (2,[5,6]) ]
B>-- записываем слово в файл "?.txt" если слово содержит букву ?
B>-- дополнительно слова, длиной 3 пишем во временный файл
B>-- дополнительно слова, начинающиеся с гласной буквы пишем в консоль
NGGU> type Targets = String -> [Target]
NGGU> targets3 :: Targets
B>targets3 word = byContain word ++ byLen3 word ++ byVowel word
B>  where
B>    byContain letters = [File $ s:".txt" | s <- letters]
B>    byLen3 letters | length letters == 3 = [TempFile]
B>                   | otherwise = []
B>    byVowel letters | head letters `elem` ['e','u','i','o','a'] = [Console]
B>                    | otherwise = []
B>


B>Обработку мы настраиваем двумя функциями (передаются в качестве параметра processWord):

B>1. targets — по слову говорит, куда это слово надо записать (в консоль, в конкретный файл, никуда)
NGGU> (Кстати, так задумано, что слово с 3 буквами 'e' попадает в файл с именем 'e' трижды?)
B>2. process — выполняет саму обработку группы слов

Во всех предложенных решениях, кроме, разве что совсем не кастомизируемых, выбор списка для представления элементов группы (а именно его иммутабельность) приводит к необходимости создания конструкции вида [(ID, String)], где ID — идентификатор группы (от вырожденных случаев где ID — имя файла, до сложных — где ID содержит информацию о способе обработки), и необходимости эту конструкцию преобразовывать к виду — [(ID, [String])].
Это иллюстрация того, как выбор "архитектурного решения" (дизайн, если хотите) оказывается взависимости от выбора языка реализации, хотя, казалось бы, такого быть не должно :)

Есть, так же, довольно любопытное мнение, озвучиваемое в л-ре посвящённой ооп, согласно которому, функциональная декомпозиция имеет существенный недостаток перед оо-декопозицей. Утверждается, что при ф-декомпозиции разбиение на подзадачи зависит от выбора "главной функции" системы (в нашей задаче, честно говоря, выбор этой функции очевиден :)) и нет никакой гарантии, что при реализации новой функции получится использовать подзадачи полученные входе первоначального решения -> либо часть функциональности будет повторена, либо потребуется провести декомпозицию всего решения заново, чтобы выделить общие подзадачи...
С этим утверждением можно согласиться, когда речь идёт о "классических" процедурных языках, но в случае с современными функциональными языками, где есть фвп и прочии прелести, можно и поспорить. Чтобы опровергнуть его, нужно привести какие-то доводы в пользу того, что при декомпозиции будут полученны подзадачи имеющие самостоятельное значение для предметной области над которой определена исходная задача (а не для вычислителя). В случае ОО-декомпозиции, этого пытаются достич моделируя сущности/концепции (называйте как хотите) из предметной области в виде объектов и классов объектов, а отнощения между ними — в виде методов классов, статических методов или "синтетических" объектов. В итоге образуется набор "кубиков" из которых затем строится решение конкретной задачи. Поскольку изменения в предметной области случаются реже, чем формулируются новые задачи, вероятность использовать при решении новой задачи существующие "кубики" достаточна велика, что, в каком-то виде, гарантирует устойчивость решения к изменениям в требованиях и упрощение реализации новых требований через повторное использование не только результатов анализа предметной области, но и имеющегося кода. Минусы тут тоже есть:
— во-первых, выделение сущностей и отношений между ними процесс "творческий" и если нет достаточного опыта или таланта, то результат может оказаться плачевным.
— во-вторых, не всегда "изменения в предметной области случаются реже, чем формулируются новые задачи" -> для таких областей нужен другой подход к проектированию.
— в-третьих, существует граница, когда написать решение с нуля проще, чем разбираться во всех имеющихся "кубиках" (это относится не только к ООП, но и к ФП :))
— в-четвёртых, с определённого уровня вложенности подзадачи перестают относиться к предметной области и превращаются в "борьбу" с техническими сложностями/особенностями языка разработки и вспомогательными библиотеками (стандартными/сторонними).
Тем не менее, худо-бедно с этими минусами справиться удаётся.

В случае с ФП всё не так прозрачно. Есть требуется решить "конкретную" задачу, то всё понятно. Задачу крошим на подзадачи и красиво расписываем. Если нужно создать основу для решения родственных задач — тоже понятно — формулируется "конкретная" задача, где описываются вариации, далее задача решается "обычным" путём. Всё хорошо, но только "точки вариации" должны быть описаны _заранее_ (в отличии ОО подхода, когда "все возможные" вариации задач закладываются в модели предметной области (в классах и методах) без попытки перечислить их _все_ явно).
Булат озвучил подход при котором ФЯ используется как средство для описания dsl, на котором затем решается задача. Этот подход отличается от разбиения задачи на подзадачи и может в теории (так же как и ООП — _в теории_ :)) дать гарантии "устойчивости" о которых выше шла речь, т.к. язык предметной области должен, по сути, отражать теже сущности и отношения между ними за которыми гоняется ООП, только в виде отличном от классов и методов (и может быть даже более красивым способом) и убирать на задний план "борьбу" языком и библиотеками. Правда, в этом случае возникает вопрос "как придумать хороший dsl"... но отложим его в сторону до лучших времен

А подумалось вот о чём: может если использовать идею о dsl, то "дизайн" несчастной задачи про слова не зависил бы от языка, как указывалось выше? Надо бы попробовать :)

Итак, нужно выбрать способ формального описания условия задачи, а затем реализовать преобразование формального описания в описание на яп.
Отталкиваться будем от того, что критерий разбивки слов на группы часто меняется, поэтому dsl должен позволять быстро реализовывать новые варианты. Можно сделать так:

StartWith "a" -> WriteToFile "a"
StartWith "b" -> WriteToFile "b"
...
StartWith "z" -> WriteToFile "z"
Сontains "c" -> WriteToFile "Contains 'c | d'"
Сontains "d" -> WriteToFile "Contains 'c | d'"
StartWith "aaa" -> WriteToFile "aaa"
... 
StartWith "paa" -> WriteToConsole "test", WriteToFile "paa"
StartWith "pab" -> WriteToFile "aaa"
...
StartWith "zzz" -> WriteToFile "zzz"


Очевидно, что требуются примитивы для упрощения ввода "программы":

StartWith x -> WriteToFile x, x in ["a" .. "z"] 

Сontains "c" | Сontains "d" -> WriteToFile "Contains 'c | d'"

Match (wordPrefix 3) | "paa"  -> WriteToConsole "debug", WriteToFile "paa"  
                     | prefix -> WriteToFile prefix

Почесав репу, можно добавить нотацию для конфигурирования процесса группировки:

StartWith x -> WriteToFile x {unique: true}, x in ["a" .. "z"] 
-- ^^ в файл пишутся только уникальные строки (дубликаты игнорируются и в другие группы не попадают)
 
Сontains "c" | Сontains "d" {unique: true} -> WriteToFile "Contains 'c | d'"  
-- ^^ матчатся только уникальные строки (дубликаты не матчатся и могут попасть в другие группы)

Match (wordPrefix 3) | "paa"  -> WriteToConsole "debug", WriteToFile "paa"  
                     | prefix -> WriteToFile prefix

Думаю, что такой-же синтаксис можно было бы, чуть-чуть изменив, свести к синтаксису хаскелл. А как насчёт ООЯ?

В java получилось бы что-то такое:
b = new RuleBuilder()
for(String x : new Range("a", "z")) {
    b.addRule(new StartWith(x), 
              new WriteToFile(x).unique(true));
}

b.addRule(new Or(new Contains("c"), new Contains("d")).unique(true), 
          new WriteToFile("Contains 'c | d'"));

b.addRule(new WordPrefixRule(3) {
              Action createDefaultAction(String prefix) { 
                     return new WriteToFile(prefix); 
              }
          }.add("paa", new WriteToConsole("debug"), new WriteToFile("paa")));

b.process(theData);


Конечному пользователю, конечно, не отдашь, но если бы была выгода от использования текстового dsl, то от этого чуда её тоже можно было бы поиметь.

B>На самом деле, то же можно повторить и в ООП :) Разный подход будет в "настройке".

B>Думаю решение на ООП будет выглядеть чуть по сложнее, т.к., на мой взгляд, функцию легче охватить взглядом и понять, чем класс ООП.
B>Не говоря уже о добавлении нескольких классов / интерфейсов.

От смотрящего многое зависит. Имхо, если классы/интерфейсы не с неба брались, то ситуация аналогична ситуации со скобками в лиспе: кто-то без них жить не может, кто-то принципиально не перевариавает :)
Re: Мастер-класс по ФП
От: MasterZiv СССР  
Дата: 09.01.09 15:14
Оценка: -1
BulatZiganshin пишет:

> итак, попробуйте решить эти задачи:

>
> 1) есть список слов. надо записать в файл "a" все слова, начинающиеся на
> букву 'a', в файл "b" — слова на 'b' и т.д. вплоть до z

Я что=то не вижу тут особенно функционального программирования.
Декларативное — при возможности делать destructuring строк — может быть.
Тут даже функций высших порядков не применить, кажется.

;;;; -*- Mode: Lisp; Syntax: Common-Lisp; Package: fp-train-1 -*-

(defpackage #:fp-train-1 (:use :cl))

(in-package #:fp-train-1)

(defun 1-st-letter (word)
   (cond ((symbolp word) (char (symbol-name word) 0))
    ((stringp word) (char word 0))
    ((characterp word) word)
    (t word)))

(let ((files nil))
   (defun letter-file (letter)
     (let ((file (cdr (assoc letter files))))
       (if file
      file
      (let ((file (open (string letter) :direction :output)))
        (setf files (acons letter file files))
        file))))
   (defun finish ()
     (mapc (lambda (e) (close (cdr e))) files)))

(defun cast-words (word-list)
   "Casts all words in WORD-LIST by files named A, B, ... Z according to their 
first letter"
   (unwind-protect
        (mapc
    (lambda (word)
      (format (letter-file (1-st-letter word)) "~A~%" word))
    word-list)
     (finish)))
Posted via RSDN NNTP Server 2.1 beta
Re[3]: Мастер-класс по ФП
От: MasterZiv СССР  
Дата: 09.01.09 15:21
Оценка:
BZ>3-я задача: есть функция сжатия compress :: String -> String. ей на вход передаётся строка (или буфер и его размер — это непринципиально), а она возвращает сжатые данные. и есть аналогичная функция decompress. при этом они однопоточные. а у нас 4-процессорная машина. соответственно, нужно написать программу, читающую входные данные мегабайтными блоками, и упаковывающую "в 4 руки", и к ней программу распаковки

Гы, а что значит "однопоточные функции" без контекста языка программирования ?

Ты уж того, давай другую какую-то задачу. Если вся сложность данной в том, чтобы преодолеть нериэнтерабельность
данных двух функций, но не фиксирован язык программирования, то задача как-то очень странна.
Re[4]: Мастер-класс по ФП
От: MasterZiv СССР  
Дата: 09.01.09 15:28
Оценка:
BB>На плюсах.
BB>Вариант №1. Do not optimize prematurely...

Плюсы не так и плохо выглядят. Конечно, тут упрощено сильно всё, но всё же.
Это ещё раз доказывает, что задача -- не для функционального языка. Функциональные
задачи на С++ решаются хреновенько -- не тот язык. Хотя можно и там.
Re[4]: Мастер-класс по ФП
От: MasterZiv СССР  
Дата: 09.01.09 15:30
Оценка:
Здравствуйте, Basil B, Вы писали:

BB>На плюсах.


BB>

BB>ofstream* ofs[26];

BB>for(char c = 'a'; c <= 'z'; ++c)
BB>{
BB>    ofs[c - 'a'] =  new ofstream(string(1, c).c_str());
BB>}

BB>vector<string> words;

BB>for(int i = 0, end = words.size(); i != end; ++i)
BB>{
BB>    *ofs[words[i][0] - 'a'] << words[i] << ' ';
BB>}
BB>


А файлы закрывать кто будет ? А память удалять ? Не, там должно быть всё сложнее.
Re[4]: Мастер-класс по ФП
От: BulatZiganshin  
Дата: 09.01.09 15:41
Оценка:
Здравствуйте, MasterZiv, Вы писали:


BZ>>3-я задача: есть функция сжатия compress :: String -> String. ей на вход передаётся строка (или буфер и его размер — это непринципиально), а она возвращает сжатые данные. и есть аналогичная функция decompress. при этом они однопоточные. а у нас 4-процессорная машина. соответственно, нужно написать программу, читающую входные данные мегабайтными блоками, и упаковывающую "в 4 руки", и к ней программу распаковки


MZ>Гы, а что значит "однопоточные функции" без контекста языка программирования ?


MZ>Ты уж того, давай другую какую-то задачу. Если вся сложность данной в том, чтобы преодолеть нериэнтерабельность

MZ>данных двух функций, но не фиксирован язык программирования, то задача как-то очень странна.

проблема в том, что эти функции задействуют только одно ядро, соответственно скорость сжатия получается вчестверо меньше, чем могло бы быть при задействовании всех ядер. соответственно, задача состоит в том, чтобы читать данные кусками по мегабайту, сжимать до 4 кусков одновременно, и затем записывать сжатые данные на диск

вот тесты мой собственной реализации этого процесса:

было:

D:\> Arc.exe create a enwik8 -m2 -t
Compressed 1 file, 100.000.000 => 26.576.090 bytes. Ratio 26.5%
Compression time 8.36 secs. Real time 8.92 secs, speed 11.212 kB/s
Testing time 10.69 secs. Real time 11.19 secs, speed 8.940 kB/s

стало:

C:\> Arc.exe create a enwik8 -m2 -t
Compressed 1 file, 100.000.000 => 26.576.090 bytes. Ratio 26.5%
Compression time 9.22 secs. Real time 2.63 secs, speed 37.965 kB/s
Testing time 14.12 secs. Real time 3.85 secs, speed 25.994 kB/s
Люди, я люблю вас! Будьте бдительны!!!
Re[2]: Мастер-класс по ФП
От: MasterZiv СССР  
Дата: 09.01.09 17:22
Оценка:
MasterZiv пишет:

Или если попроще, так:

(defun cast-words-trivial (word-list)
   (mapc
    (lambda (word)
      (let ((file  (open (string (char word 0))
            :direction :output
            :if-does-not-exist :create
            :if-exists :append)))
        (unwind-protect
        (format file "~A~%" word)    
     (when file (close file)))))
    word-list))
Posted via RSDN NNTP Server 2.1 beta
Re[5]: Мастер-класс по ФП
От: MasterZiv СССР  
Дата: 09.01.09 17:24
Оценка:
BulatZiganshin пишет:

> проблема в том, что эти функции задействуют только одно ядро,

> соответственно скорость сжатия получается вчестверо меньше, чем могло бы

Какие функции, какое ядро, не понимаю ничего.
Posted via RSDN NNTP Server 2.1 beta
Re[6]: Мастер-класс по ФП
От: BulatZiganshin  
Дата: 09.01.09 18:23
Оценка:
Здравствуйте, MasterZiv, Вы писали:

MZ>BulatZiganshin пишет:


>> проблема в том, что эти функции задействуют только одно ядро,

>> соответственно скорость сжатия получается вчестверо меньше, чем могло бы

MZ>Какие функции, какое ядро, не понимаю ничего.


а ты вообще когда-нибудь пробовал программировать для многоядерных процессоров?
Люди, я люблю вас! Будьте бдительны!!!
Re[5]: Подумалось...
От: Beam Россия  
Дата: 09.01.09 20:30
Оценка:
Здравствуйте, NotGonnaGetUs, Вы писали:

NGG>Во всех предложенных решениях, кроме, разве что совсем не кастомизируемых, выбор списка для представления элементов группы (а именно его иммутабельность) приводит к необходимости создания конструкции вида [(ID, String)], где ID — идентификатор группы (от вырожденных случаев где ID — имя файла, до сложных — где ID содержит информацию о способе обработки), и необходимости эту конструкцию преобразовывать к виду — [(ID, [String])].


Вообще-то другого здесь и не дано. По своей сути — это задача классификации.
А классификация — это установление зависимости между элементами и классами. Отсюда появляется [(ID, element)]
Классификация не нужна сама по себе. Мы должны что-то сделать с группами. Отсюда появляется [(ID, [element])]

NGG>Это иллюстрация того, как выбор "архитектурного решения" (дизайн, если хотите) оказывается взависимости от выбора языка реализации, хотя, казалось бы, такого быть не должно


Мне непонятно, что понимается под дизайном. И непонятна иллюстрация "того, как выбор "архитектурного решения" (дизайн, если хотите) оказывается взависимости от выбора языка реализации".
По-моему, при обобщении задачи классификации, так или иначе, решения на любом языке придут к чему-то похожему выше.
Разница лишь в том, как эти данные будут представлены языке. Как список в ФЯ или как класс группа, агрегирующий свои элементы, в ООП.

NGG>Есть, так же, довольно любопытное мнение, озвучиваемое в л-ре посвящённой ооп, согласно которому, функциональная декомпозиция имеет существенный недостаток перед оо-декопозицей. Утверждается, что при ф-декомпозиции разбиение на подзадачи зависит от выбора "главной функции" системы (в нашей задаче, честно говоря, выбор этой функции очевиден и нет никакой гарантии, что при реализации новой функции получится использовать подзадачи полученные входе первоначального решения -> либо часть функциональности будет повторена, либо потребуется провести декомпозицию всего решения заново, чтобы выделить общие подзадачи...

NGG>С этим утверждением можно согласиться, когда речь идёт о "классических" процедурных языках, но в случае с современными функциональными языками, где есть фвп и прочии прелести, можно и поспорить. Чтобы опровергнуть его, нужно привести какие-то доводы в пользу того, что при декомпозиции будут полученны подзадачи имеющие самостоятельное значение для предметной области над которой определена исходная задача (а не для вычислителя).

Про какие подзадачи идет речь? Вот например есть функция.
обработатьГруппы(элементы, классификатор, обработчик) = обработатьКаждуюГруппу(обработчик, разбитьНаГруппы(элементы, классификатор))

Где здесь подзадачи?. Эта функция решает обощенную задачу и определяет требования к классификатору и обработчику (аналог интерфейса).
А сами функции обработчик и классификатор будут написаны для каждой предметной области уже потом.

NGG>В случае ОО-декомпозиции, этого пытаются достич моделируя сущности/концепции (называйте как хотите) из предметной области в виде объектов и классов объектов, а отнощения между ними — в виде методов классов, статических методов или "синтетических" объектов. В итоге образуется набор "кубиков" из которых затем строится решение конкретной задачи. Поскольку изменения в предметной области случаются реже, чем формулируются новые задачи, вероятность использовать при решении новой задачи существующие "кубики" достаточна велика, что, в каком-то виде, гарантирует устойчивость решения к изменениям в требованиях и упрощение реализации новых требований через повторное использование не только результатов анализа предметной области, но и имеющегося кода.


В том-то и дело, что классы привязаны к предметной области, а вот функции нет (ну почти )
А чтобы отвязать их, появляются классы, которые имеют в себе лишь поведение (по сути функция), и классы хранящие только данные (параметры функций).

NGG>В случае с ФП всё не так прозрачно. Есть требуется решить "конкретную" задачу, то всё понятно. Задачу крошим на подзадачи и красиво расписываем. Если нужно создать основу для решения родственных задач — тоже понятно — формулируется "конкретная" задача, где описываются вариации, далее задача решается "обычным" путём. Всё хорошо, но только "точки вариации" должны быть описаны _заранее_ (в отличии ОО подхода, когда "все возможные" вариации задач закладываются в модели предметной области (в классах и методах) без попытки перечислить их _все_ явно).


Мне не совсем понятно, где вариации описаные заранее. Вон в том же примере "обработатьГруппы".
Описаны требования (интерфейсы), но не сами вариации. То же самое и в ООП. Не вижу я разницы.
... << RSDN@Home 1.2.0 alpha 4 rev. 1136>>
Best regards, Буравчик
Re[6]: Подумалось...
От: NotGonnaGetUs  
Дата: 10.01.09 03:32
Оценка:
Здравствуйте, Beam, Вы писали:

B>Вообще-то другого здесь и не дано. По своей сути — это задача классификации.

B>А классификация — это установление зависимости между элементами и классами. Отсюда появляется [(ID, element)]
B>Классификация не нужна сама по себе. Мы должны что-то сделать с группами. Отсюда появляется [(ID, [element])]

NGG>>Это иллюстрация того, как выбор "архитектурного решения" (дизайн, если хотите) оказывается взависимости от выбора языка реализации, хотя, казалось бы, такого быть не должно


B>Мне непонятно, что понимается под дизайном. И непонятна иллюстрация "того, как выбор "архитектурного решения" (дизайн, если хотите) оказывается взависимости от выбора языка реализации".


"Задача классификации" — это всё-таки не наша задача.

Наша задача состоит в том, чтобы "раскидать" данные по обработчикам согласно определённому набору критериев.
Причём "в лоб" (в императивном стиле) эта задача решается с использованием O(1) доп.памяти и оценкой O(N) (если исключить монстрообразные критерии, требующие хранения состояния и сложных вычислений).

И нигде не звучит требования построить список [(ID, element)] или список [(ID, [element])] в явном виде, что тянет за собой как минумум O(N)/O(NlogN) по памяти/времени и, думается, с "хорошей" С перед оценкой времени.

Вопрос: чем руководствовался "дизайнер" принимая решение так разбить задачу на подзадачи? Вариантов ответа, конечно же, можно придумать много, как оскорбительных, так и наоборот . Имхо, мысль течёт примерно так:
— "грязь" с выводом в файл скрываем за функцией String -> [String] -> IO(),
— значит в "чистом" коде нужно получить перечисление [(String, [String])]
— инкрементально такую структуру не построить (добавляя по слову), значит нужно промежуточное представление, которое можно строить быстро. Такая структура есть [(String, String)]
— ну, а дальше sortBy, который затягивает все слова в память и даёт N*logN

Т.е. декомпозиция (выделение подзадач) основывается на особенностях языка (грязь отдельно, иммутабельные структуры данных, особенности синтаксиса), а не на анализе задачи.

Можно возразить, что решение демонстрирует как красиво/лаконично/и т.п. писать на ФЯ. Ок, но это значит, что решение тем более построенно исходя из выразительных особенностей языка реализации, а не на основе анализа задачи... ч.т.д

B>По-моему, при обобщении задачи классификации, так или иначе, решения на любом языке придут к чему-то похожему выше.

B>Разница лишь в том, как эти данные будут представлены языке. Как список в ФЯ или как класс группа, агрегирующий свои элементы, в ООП.

Я бы не стал утверждал, что в любом решении будет содержаться подзадача преобразовывать [(ID, element)] -> [(ID, [element])].

B>Про какие подзадачи идет речь? Вот например есть функция.

B>
B>обработатьГруппы(элементы, классификатор, обработчик) = обработатьКаждуюГруппу(обработчик, разбитьНаГруппы(элементы, классификатор))
B>

B>Где здесь подзадачи?. Эта функция решает обощенную задачу и определяет требования к классификатору и обработчику (аналог интерфейса).
B>А сами функции обработчик и классификатор будут написаны для каждой предметной области уже потом.

Как это где?

1) Построить классификатор и обработчик в соответствии с условием задачи (далеко не факт, что это элементарно сделать).
2) разбитьНаГруппы используя классификатор
3) обработатьКаждуюГруппу используя обработчик

B>В том-то и дело, что классы привязаны к предметной области, а вот функции нет (ну почти )

B>А чтобы отвязать их, появляются классы, которые имеют в себе лишь поведение (по сути функция), и классы хранящие только данные (параметры функций).

"Функция" может быть отвязана от предметной области, только если она принадлежит "нижележащему" уровню абстракции/обвязке решения, н-р, привязана к collection api языка.
Такие функции не требуется откуда-то отвязывать, т.к. это либо не имеет смысла (н-р, map, filter, foldr в java всё равно удобнее делать через цикл), либо уже давно отвязано и помещено в стандартные или общеупотебимые библиотеки.

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

NGG>>Всё хорошо, но только "точки вариации" должны быть описаны _заранее_ (в отличии ОО подхода, когда "все возможные" вариации задач закладываются в модели предметной области (в классах и методах) без попытки перечислить их _все_ явно).


B>Мне не совсем понятно, где вариации описаные заранее. Вон в том же примере "обработатьГруппы".

B>Описаны требования (интерфейсы), но не сами вариации. То же самое и в ООП. Не вижу я разницы.

Описав требования (интерфейсы), ты как раз и задал точки вариации для решения — то, как его можно расширять.
В ООП интерфейс является такой же точкой расширения, верно.

А теперь представь, что задача изменилась так, что недостаточно просто реализовать интерфейсы, чтобы получить решение.
В случае ООП, для решения новой задачи можно будет, как минимум, использовать классы из модели предметной области — базовый строительный материал.
А в случае ФП? Если декомпозиция на подзадачи велась исходя из деталей алгоритма решения конкретной задачи, нет никакой гарантии, что получившиеся подзадачи смогут пригодиться где-то ещё.
Вся разница в этом месте.

Проблема тут скорее теоретическая, чем практическая, т.к. на практике рулит кисс, сроки и чтобы работало
Re[7]: Подумалось...
От: D. Mon Великобритания http://thedeemon.livejournal.com
Дата: 10.01.09 13:49
Оценка:
Здравствуйте, NotGonnaGetUs, Вы писали:

NGG>А теперь представь, что задача изменилась так, что недостаточно просто реализовать интерфейсы, чтобы получить решение.

NGG>В случае ООП, для решения новой задачи можно будет, как минимум, использовать классы из модели предметной области — базовый строительный материал.
NGG>А в случае ФП? Если декомпозиция на подзадачи велась исходя из деталей алгоритма решения конкретной задачи, нет никакой гарантии, что получившиеся подзадачи смогут пригодиться где-то ещё.

А в чем принципиальное различие? В случае ООП у нас есть классы, т.е. данные и функции для работы с ними. В случае ФП у нас есть то же самое — данные и функции для работы с ними. Иногда они объединены в одну синтаксическую структуру, например модуль, иногда просто описаны рядом. Как я понимаю, есть опасения, что в случае ФП функции для работы с данными не будут покрывать все возможные операции, а будут заточены на задачу. Но и в ООП случае гарантии универсальности описанных методов нет — для одной задачи данного набора методов оказалось достаточно, для другой могут потребоваться новые..
Re[5]: Мастер-класс по ФП
От: Mr.Cat  
Дата: 10.01.09 13:58
Оценка:
Здравствуйте, BulatZiganshin, Вы писали:

BZ>было:


BZ>D:\> Arc.exe create a enwik8 -m2 -t

BZ>Compressed 1 file, 100.000.000 => 26.576.090 bytes. Ratio 26.5%
BZ>Compression time 8.36 secs. Real time 8.92 secs, speed 11.212 kB/s
BZ>Testing time 10.69 secs. Real time 11.19 secs, speed 8.940 kB/s

BZ>стало:


BZ>C:\> Arc.exe create a enwik8 -m2 -t

BZ>Compressed 1 file, 100.000.000 => 26.576.090 bytes. Ratio 26.5%
BZ>Compression time 9.22 secs. Real time 2.63 secs, speed 37.965 kB/s
BZ>Testing time 14.12 secs. Real time 3.85 secs, speed 25.994 kB/s

Мне кажется, или Вы в итоге только проиграли за счет времени, проведенного в ядре (на блокировках, похоже)?
Re[6]: Мастер-класс по ФП
От: VoidEx  
Дата: 10.01.09 14:08
Оценка:
Здравствуйте, Mr.Cat, Вы писали:

MC>Мне кажется, или Вы в итоге только проиграли за счет времени, проведенного в ядре (на блокировках, похоже)?

Было-то 8.92, стало 2.63, как же проиграли? Прирост 3.4х. Не 4х, конечно, но всё же.
Re[6]: Мастер-класс по ФП
От: BulatZiganshin  
Дата: 10.01.09 14:48
Оценка:
Здравствуйте, Mr.Cat, Вы писали:

BZ>>Compression time 8.36 secs. Real time 8.92 secs, speed 11.212 kB/s

BZ>>стало:
BZ>>Compression time 9.22 secs. Real time 2.63 secs, speed 37.965 kB/s

MC>Мне кажется, или Вы в итоге только проиграли за счет времени, проведенного в ядре (на блокировках, похоже)?


первая цифра — cpu time, время всех четырёх ядер. вторая — wall clock time, время от запуска программы до завершения. проигрыш в первом времени — обычное явление, результат нехватки пропускной способности памяти. грубо говоря, если бы память была бесконечно быстра, то первое время не возросло бы и мы бы смогли ускориться ровно вчетверо
Люди, я люблю вас! Будьте бдительны!!!
Re[8]: Подумалось...
От: NotGonnaGetUs  
Дата: 10.01.09 18:59
Оценка:
Здравствуйте, D. Mon, Вы писали:

DM>А в чем принципиальное различие? В случае ООП у нас есть классы, т.е. данные и функции для работы с ними. В случае ФП у нас есть то же самое — данные и функции для работы с ними. Иногда они объединены в одну синтаксическую структуру, например модуль, иногда просто описаны рядом.



Разница в том, откуда берутся "данные" и "функции для работы с ними".
В ООП — из анализа предметной области (так говорит теория) => "классы" полученые для решения задачи А, останутся полезны для решения задачи Б из той же области (учитываем, что ооп применяется для создания систем, где основная сложность не алгоритмическая, а "размерная" — предметная область стоит из большого числа сущностей и отношений, а возникающие задачи относительно просты, но их много).
В ФП — как результат декомпопозиции _конкретной_ задачи на подзадачи (какими методами гуру пользуются в действительности — не знаю). Подзадачи же, в большой степени отражают алгоритм выбранный для решения исходной задачи, чем значимый аспект предметной области... (думаю, что гуру должны знать рецепты, как такого можно избежать, но статей на эту тему не видел).


DM> Как я понимаю, есть опасения, что в случае ФП функции для работы с данными не будут покрывать все возможные операции, а будут заточены на задачу. Но и в ООП случае гарантии универсальности описанных методов нет — для одной задачи данного набора методов оказалось достаточно, для другой могут потребоваться новые..


Дело не в том, что "могут потребоваться новые" (они потребуются при любом раскладе), а в том, насколько их реализация будет проста и как повлияет на систему в целом.

В случае ООП новые требования приводят к расширению и уточнению модели, а т.к. модель в значительной степени отражается 1-1 в "классы", новые требования не ведут к необходимости существенно изменять базу кода и не влияют на существующие решения... (до тех пор, пока изменения не коснулись самой предметной области или допущений сделанных относительно неё , но это другая песня).

В случае ФП — х3. На простых примерах всё выглядит здорово, кисс работает. Что будет если использовать фп там, где сейчас используется java — могу только гадать.
Пока считаю, что овчинка выделки не стоит, но надежд на лучшее не теряю Будь возможность выбора, по вполне очевидным причинам, предпочёл бы на замену java'е скалу, а не хаскелл, что дало бы (на вскидку) двухкратное сокращение затрат на кодирование (для тех задач, что мне приходится решать).
Re[9]: Подумалось...
От: BulatZiganshin  
Дата: 10.01.09 19:15
Оценка: 1 (1) +1 :)))
Здравствуйте, NotGonnaGetUs, Вы писали:

NGG>В ООП — из анализа предметной области (так говорит теория) => "классы" полученые для решения задачи А, останутся NGG>В ФП — как результат декомпопозиции _конкретной_ задачи на подзадачи (какими методами гуру пользуются в


папа, а чем люди дышали до открытия кислорода?

Люди, я люблю вас! Будьте бдительны!!!
Re[10]: Подумалось...
От: NotGonnaGetUs  
Дата: 10.01.09 20:40
Оценка:
Здравствуйте, BulatZiganshin, Вы писали:

BZ>

папа, а чем люди дышали до открытия кислорода?


Воздухом!

А после открытия кислорода и ряда исследований научились дышать и в безвоздушном пространстве
Re[5]: Мастер-класс по ФП
От: Tonal- Россия www.promsoft.ru
Дата: 13.01.09 12:11
Оценка:
Здравствуйте, BulatZiganshin, Вы писали:
T>>>>4. Отфильтровать дерево, оставив только ветви содержащие дубликаты и сами дубликаты.
T>>Как подправить, сходу не соображу.
BZ>строй список дубликатов локально
Ага, разобрался. Красиво получается.

Возник ещё связанный вопрос:
При поиске дубликатов нужно сравнивать не полностью строку, а некоторую её часть.
Вроде бы ничего сложного — пишем функцию выделения существенной части строки (getIdent) и сипользуем именно её результат для поиска.

Строкиа имеет такой вид (prel):
/[\w\s]+(\([^)]+\)\s*)*/

Т.е. в начале несколько слов разделённых пробелами, и потом возможно несколько групп скобок.

Первый выриант getIdent был такой:
ltrim = dropWhile (==' ')
trim = reverse . ltrim . reverse . ltrim

getIdent = trim . takeWhile (/='(')

На моём объёме файла дерева ~8мб. выполнялось вполне шустро.

Теперь потребовалось включить в идентификатор первый символ из последней группы скобок, если она имеет такой вид (perl):
/\(\w,\s+[^)]+\)/

Т. е. getIdent стал такой:
getIdent str = first ++ getSmb other
  where
    first = trim $ takeWhile (/='(') str
    other = last $ splitBy (=='(') str
    getSmb (x:y:z:xs) | x == '(' && z == ',' = " (" ++ [y] ++ ")"
    getSmb xs = ""

splitBy _ [] = []
splitBy f x = fist : splitBy f other
  where
    (fist, other) = break f x

И вот окончания его работы дождатся не получается.

Как ускорить этот кусок, и вообще как лучше работать с большими объёмами строк?
... << RSDN@Home 1.2.0 alpha 4 rev. 0>>
Re: Мастер-класс по ФП
От: D. Mon Великобритания http://thedeemon.livejournal.com
Дата: 14.01.09 04:43
Оценка:
Предлагаю продолжить на примере такой задачи:

1. Есть большой текстовый файл A (несколько гигов, заведомо не помещается в память), нужно сделать текстовый файл A1, содержащий все строки из А, но отсортированные в алфавитном порядке. Ограничение по памяти — 256 МБ.

2. Изменить первую программу так, чтобы будучи запущена с ключом -u она создавала файл А2, содержащий все уникальные строки из А в алфавитном порядке и количество повторений для каждой строки. Т.е. из
dd ee ff
aaa bbb ccc
aaa bbb ccc
dd ee ff
aaa bbb ccc

Получилось бы
3: aaa bbb ccc
2: dd ee ff

Ограничения на память те же.
Re[4]: Мастер-класс по ФП
От: Tonal- Россия www.promsoft.ru
Дата: 14.01.09 05:53
Оценка:
Здравствуйте, Basil B, Вы писали:
dmz>>Ну так можно привести тривиальное сишное решение хотя бы первой задачи?
BB>Вариант №2.
Надо примерно так:
#include <iostream>
#include <fstream>
#include <iterator>
#include <cctype>

using namespace std;

typedef istream_iterator<string> word_iter_t;

int main() {
  ofstream ofs[26];
  for(char c[2] = "a"; c[0] <= 'z'; ++c[0]) 
    ofs[c[0] - 'a'].open(c);

  std::ifstream words("words.txt");
  for(word_iter_t cur(words), end; cur != end; ++cur)
    if (cur->size() > 0 && isalpha((*cur)[0]) && islower((*cur)[0]))
      ofs[(*cur)[0] - 'a'] << *cur << ' ';
}

Вроде как вполне компактно и прозрачно.
Все ресурсы закрываются, память не течёт.

Для трёх буквочек можно нарисовать примерно так:
#include <iostream>
#include <fstream>
#include <iterator>
#include <cctype>
#include <map>
#include <boost/shared_ptr.hpp>

using namespace std;

typedef istream_iterator<string> word_iter_t;
typedef boost::shared_ptr<ofstream> ostream_ptr_t;
typedef map<string, ostream_ptr_t> omap_t;

int main() {
  omap_t ofs;
  ifstream words("words.txt");
  for(word_iter_t cur(words), end; cur != end; ++cur) {
    string fname = cur->substr(0, 3);
    if (fname.size() == 0) //Остальные проверки валидности имени и слова.
      continue;
    if (ofs.find(fname) == ofs.end())
      ofs.insert(make_pair(
        fname, ostream_ptr_t(new ofstream(fname.c_str()))));
    ofstream& out = *ofs.find(fname)->second.get();
    out << *cur << ' ';
  }
}
... << RSDN@Home 1.2.0 alpha 4 rev. 0>>
Re[6]: Мастер-класс по ФП
От: Tonal- Россия www.promsoft.ru
Дата: 14.01.09 09:12
Оценка:
Здравствуйте, Tonal-, Вы писали:
T>И вот окончания его работы дождатся не получается.
Нашел ошибочку.
Функция splitBy циклилась.
На самом деле она идентична первоначальной groupFrom без проверки критерия для первого элемента.
getIdent str = first ++ (getSmb $ latest end)
  where
    first = strip $ takeWhile (/='(') str
    end = groupFrom (=='(') str
    latest [] = ""
    latest xs = last xs 
    getSmb (x:y:z:xs) | x == '(' && z == ',' = " (" ++ [y] ++ ")"
    getSmb xs = ""


T>... как лучше работать с большими объёмами строк?

Но этот вопрос таки остаётся.
Нужен поиск и замена подстроки.
... << RSDN@Home 1.2.0 alpha 4 rev. 0>>
Re[5]: Мастер-класс по ФП
От: BulatZiganshin  
Дата: 14.01.09 09:44
Оценка:
Здравствуйте, Tonal-, Вы писали:

T>Для трёх буквочек можно нарисовать примерно так:


ага. ты в курсе, что кол-во открытых файлов несколько ограничено?
Люди, я люблю вас! Будьте бдительны!!!
Re[7]: Мастер-класс по ФП
От: BulatZiganshin  
Дата: 14.01.09 09:45
Оценка:
Здравствуйте, Tonal-, Вы писали:

T>>... как лучше работать с большими объёмами строк?

T>Но этот вопрос таки остаётся.
T>Нужен поиск и замена подстроки.

ByteString, regexp packages
Люди, я люблю вас! Будьте бдительны!!!
Re[6]: Мастер-класс по ФП
От: MasterZiv СССР  
Дата: 14.01.09 11:48
Оценка:
BulatZiganshin wrote:

> ага. ты в курсе, что кол-во открытых файлов несколько ограничено?


Ага, а ты в курсе, что большинство предложенных решений построено
по тому же принципу ? Я-то вот кэш открытых файлов сделал.
Получилось много кода.
Posted via RSDN NNTP Server 2.1 beta
Re[2]: Мастер-класс по ФП
От: BulatZiganshin  
Дата: 14.01.09 12:36
Оценка:
Здравствуйте, D. Mon, Вы писали:

DM>Ограничение по памяти — 256 МБ.


это больше похоже не на практикум по *fp*, а на практикум по конкретным реализациям. интересоваться тем, как это делается в фп языках я бы рекомендовал только после того, как усвоен сам стиль фп программирования
Люди, я люблю вас! Будьте бдительны!!!
Re[7]: Мастер-класс по ФП
От: BulatZiganshin  
Дата: 14.01.09 14:09
Оценка:
Здравствуйте, MasterZiv, Вы писали:

>> ага. ты в курсе, что кол-во открытых файлов несколько ограничено?


MZ>Ага, а ты в курсе, что большинство предложенных решений построено

MZ>по тому же принципу ? Я-то вот кэш открытых файлов сделал.
MZ>Получилось много кода.

вообще-то я с самого начала подразумевал, что таких решений быть не должно. в фп языках не так уж сложно подготовить сначала данные в памяти чтобы затем писать по одному файлу
Люди, я люблю вас! Будьте бдительны!!!
Re[3]: Мастер-класс по ФП
От: D. Mon Великобритания http://thedeemon.livejournal.com
Дата: 14.01.09 14:19
Оценка:
Здравствуйте, BulatZiganshin, Вы писали:

BZ>это больше похоже не на практикум по *fp*, а на практикум по конкретным реализациям.


Там фишка именно в дизайне — как обобщить сортировку на разные типы файлов с их нюансами. Я такую задачу решал на окамле с помощью модулей, но возможно есть более красивое решение.

BZ>интересоваться тем, как это делается в фп языках я бы рекомендовал только после того, как усвоен сам стиль фп программирования


Тогда прошу продемонстрировать этот самый стиль. Хотя бы костяк решения.
Re[4]: Мастер-класс по ФП
От: BulatZiganshin  
Дата: 14.01.09 15:11
Оценка:
Здравствуйте, D. Mon, Вы писали:

DM>Там фишка именно в дизайне — как обобщить сортировку на разные типы файлов с их нюансами. Я такую задачу решал на окамле с помощью модулей, но возможно есть более красивое решение.


я может чего-то не понимаю, но о каком обобщении здесь идёт речь?

DM>Тогда прошу продемонстрировать этот самый стиль. Хотя бы костяк решения.


я как раз и говорю о том, что решение данной задачи не поможет развить фп-стиль программирования.
Люди, я люблю вас! Будьте бдительны!!!
Re[6]: Мастер-класс по ФП
От: Tonal- Россия www.promsoft.ru
Дата: 14.01.09 15:32
Оценка:
Здравствуйте, BulatZiganshin, Вы писали:
T>>Для трёх буквочек можно нарисовать примерно так:
BZ>ага. ты в курсе, что кол-во открытых файлов несколько ограничено?
Типа так:
#include <iostream>
#include <fstream>
#include <iterator>
#include <map>
#include <vector>

using namespace std;

typedef istream_iterator<string> word_iter_t;
typedef map<string, vector<string> > wmap_t;
typedef wmap_t::const_iterator wmap_iter_t;
typedef vector<string>::const_iterator witer_iter_t;

int main() {
  wmap_t ofs;
  ifstream words("words.txt");
  for (word_iter_t cur(words), end; cur != end; ++cur) {
    string fname = cur->substr(0, 3);
    if (fname.size() > 0)
      ofs[fname].push_back(*cur);
  }
  for (wmap_iter_t cur = ofs.begin(), end = ofs.end(); cur != end; ++cur) {
    ofstream out(cur->first.c_str());
    for (
      witer_iter_t wcur = cur->second.begin(), end = cur->second.end();
      wcur != end; ++wcur
    )
      out << *wcur << ' ';
  }
}
... << RSDN@Home 1.2.0 alpha 4 rev. 0>>
Re[2]: Мастер-класс по ФП
От: Beam Россия  
Дата: 14.01.09 21:32
Оценка: 6 (1)
Здравствуйте, D. Mon, Вы писали:

DM>1. Есть большой текстовый файл A (несколько гигов, заведомо не помещается в память), нужно сделать текстовый файл A1, содержащий все строки из А, но отсортированные в алфавитном порядке. Ограничение по памяти — 256 МБ.


import System.IO
import System.Directory
import Data.List
import Data.Maybe

-- Разбиваем файл на куски определенного размера (заданного количеством строк)
-- Куски сортируем в памяти и записываем во временные файлы
-- Объединяем временные файлы в один отсортированный файл
sortBigFile srcFile dstFile maxSize = do
  tempFiles <- splitFile maxSize srcFile
  mergeChains tempFiles dstFile
  mapM_ removeFile tempFiles  -- удаляем мусор, который насоздавали


-- Возвращает ленивый список строк, содержащихся в файле
-- Позволяет работать со строками файла как со списком
-- В то же время не загружает полностью файл в память
type Stream = [String]
stream :: Handle -> IO Stream
stream handle = do
  contents <- hGetContents handle
  return $ lines contents


-- Делит файл на отсортированные куски размеров не больше maxSize строк
-- Функция возращает список временных файлов, содержащих отсортированные куски
splitFile :: Int -> FilePath -> IO [FilePath]
splitFile maxSize srcFile = do
    h <- openFile srcFile ReadMode
    s <- stream h
    tempFiles <- splitFile' maxSize 0 s
    hClose h
    return tempFiles

-- Каждый кусок сортируется и записывается во временный файл
-- Имена временных файлов: temp0, temp1, и т.д.
-- xs - оставшиеся строки исходного файла, suffix - суффикс временного файла
splitFile' _ _ [] = return []
splitFile' maxSize suffix xs = do
    let tempFile = "temp" ++ show suffix
    let (chainLines, restLines) = splitAt maxSize xs
    writeFile tempFile (unlines $ sort chainLines)  -- сортировка и запись одного куска
    restFiles <- splitFile' maxSize (suffix+1) restLines
    return (tempFile : restFiles)


-- Объединяет отсортированные куски (fileNames) в один большой отсортированный файл dstFile
mergeChains :: [FilePath] -> FilePath -> IO ()
mergeChains fileNames dstFile = do
--  dst <- dst' dstFile
  dst <- openFile dstFile WriteMode
  files <- mapM ((flip $ openFile) ReadMode) fileNames
  strms <- mapM stream files

  mergeChains' strms dst

  mapM_ hClose files
  hClose dst

-- Каждый объединяемый файл представлен потоком (ленивым списком) строк
-- Сравниваем первые строки потоков и минимальную строку отправляем в итоговый файл
-- streams - потоки строк временных файлов
-- dstHandle - итоговый файл
mergeChains' :: [Stream] -> Handle -> IO ()
mergeChains' streams dstHandle
  | null strms = return ()
  | otherwise = do hPutStrLn dstHandle minLine
                   mergeChains' (updateStreams strms) dstHandle
  where
    strms = filter (not.null) streams         -- выкидываем обработанные потоки
    minLine = minimum $ map head strms        -- выбираем из всех потоков минимальную строку

    -- В потоке, который содержал минимальную строку, переходим к следующим строкам
    -- А остальные потоки оставляем без изменений
    updateStreams (s:ss) | head s == minLine = (tail s) : ss
                         | otherwise         = s : updateStreams ss
  
--------------
--- MAIN -----
  
-- main = sortBigFile "A" "A1" 10000


Не буду судить о пригодности этой задачи к практикуму ФП. Скажу лишь, что мне она добавила некоторых знаний.
Да и решить было просто интересно. Ну и ФП все же присутствует — вон тот же mergerChain' более-менее ФП-шный, плюс ленивость, работа с файлом как со списком и т.п.

Для тестов был создан файлик A и B, внутри которых — текст данной программы повторенный хз сколько раз.
Файл А — 11,5 Мб, 0,3 млн строк
Файл B — 91,5 Мб, 2,4 млн строк

В итоге:
обработка А — 30 временных файлов — 12 сек
обработка B — 240 временных файлов — 389 сек
Расход памяти для maxSize = 10000 строк по данным -sstderr около 15 Мб
splitFile — около 30% времени, mergerChains около 70%
В целом, около 50% времени занимает сравнение строк между собой.

Для сравнения попробовал запустить упрощенный вариант

main = do 
  content <- readFile "A"
  writeFile "A1" (unlines $ sort $ lines content)


Для файла A — 7 сек, 313 Мб памяти
Для файла B — не получился результат
Сначала сказал, хочу больше виртуальной памяти. Добавил (оказалось была отключена, я даже и не знал работал целый год
Потом просто out of memory. Я так понял из-за ограничений на количество памяти для программ в Windows
... << RSDN@Home 1.2.0 alpha 4 rev. 1136>>
Best regards, Буравчик
Re[3]: Мастер-класс по ФП
От: BulatZiganshin  
Дата: 15.01.09 02:23
Оценка: 16 (2)
Здравствуйте, Beam, Вы писали:

B>Не буду судить о пригодности этой задачи к практикуму ФП. Скажу лишь, что мне она добавила некоторых знаний.


если ограничивать число строк, а не объём занимаемой памяти — то нормально
main = do -- (Лениво) создаём отсортированные подсписки
          sublists <- fmap sort1 getContents
          -- Записываем их в файлы с именами 1..n
          for (zip (map show [1..]) pairs) (uncurry writeFile)
          -- Читаем (опять же лениво) содержимое всех ранее созданных файлов
          sublists <- mapM (fmap lines readFile.show) [1..length sublists]
          -- Сливаем их и записываем результат
          putStr (unlines (mergesort' compare sublists)

sort1 = lines >>> unfoldr (\xs -> guard (xs>[]) >> splitAt 5000 xs) >>> map (sort>>>unlines)

-- Процедуру слияния упорядоченных списков можно найти в исходниках Data.List:
mergesort' :: (a -> a -> Ordering) -> [[a]] -> [a]
mergesort' cmp [] = []
mergesort' cmp [xs] = xs
mergesort' cmp xss = mergesort' cmp (merge_pairs cmp xss)

merge_pairs :: (a -> a -> Ordering) -> [[a]] -> [[a]]
merge_pairs cmp [] = []
merge_pairs cmp [xs] = [xs]
merge_pairs cmp (xs:ys:xss) = merge cmp xs ys : merge_pairs cmp xss

merge :: (a -> a -> Ordering) -> [a] -> [a] -> [a]
merge cmp xs [] = xs
merge cmp [] ys = ys
merge cmp (x:xs) (y:ys)
 = case x `cmp` y of
        GT -> y : merge cmp (x:xs)   ys
        _  -> x : merge cmp    xs (y:ys)


на самом деле, даже если я ни в чём не ошибся — этот код будет держать все данные в памяти до победного. extra points — за изящное решение этой маааленькой проблемы
Люди, я люблю вас! Будьте бдительны!!!
Re[4]: Мастер-класс по ФП
От: BulatZiganshin  
Дата: 15.01.09 02:32
Оценка:
Здравствуйте, BulatZiganshin, Вы писали:

BZ> -- Записываем их в файлы с именами 1..n

BZ> for (zip (map show [1..]) pairs) (uncurry writeFile)

zipWithM_ writeFile (map show [1..]) sublists
Люди, я люблю вас! Будьте бдительны!!!
Re[4]: Мастер-класс по ФП
От: Beam Россия  
Дата: 15.01.09 10:36
Оценка:
Здравствуйте, BulatZiganshin, Вы писали:

BZ>если ограничивать число строк, а не объём занимаемой памяти — то нормально

BZ>
BZ>main = do -- (Лениво) создаём отсортированные подсписки
BZ>          sublists <- fmap sort1 getContents
BZ>          -- Записываем их в файлы с именами 1..n
BZ>          for (zip (map show [1..]) pairs) (uncurry writeFile)
BZ>          -- Читаем (опять же лениво) содержимое всех ранее созданных файлов
BZ>          sublists <- mapM (fmap lines readFile.show) [1..length sublists]
BZ>          -- Сливаем их и записываем результат
BZ>          putStr (unlines (mergesort' compare sublists)

BZ>sort1 = lines >>> unfoldr (\xs -> guard (xs>[]) >> splitAt 5000 xs) >>> map (sort>>>unlines)
BZ>


1. Так и не смог я это запустить. В принципе идея ясна, но хочется увидеть решение, которе хотя бы компилируется (в т.ч. импорт)
А в идеале — полный пример, с открытием, закрытием файлов и пр.

2. Я думаю ленивость здесь теряется на readFile, т.к. файл закрывается и все данные попадают в память.
... << RSDN@Home 1.2.0 alpha 4 rev. 1136>>
Best regards, Буравчик
Re[5]: Мастер-класс по ФП
От: BulatZiganshin  
Дата: 15.01.09 11:26
Оценка:
Здравствуйте, Beam, Вы писали:

B>1. Так и не смог я это запустить. В принципе идея ясна, но хочется увидеть решение, которе хотя бы компилируется (в т.ч. импорт)


ну симпортируйте стандартные Data.List, Control.Monad и прочая

B>А в идеале — полный пример, с открытием, закрытием файлов и пр.


это полный пример

B>2. Я думаю ленивость здесь теряется на readFile, т.к. файл закрывается и все данные попадают в память.


нет. readFile = openFile >>= hGetContents, т.е. он читает файл так же лениво и безалаберно
Люди, я люблю вас! Будьте бдительны!!!
Re: Мастер-класс по ФП
От: xonixx  
Дата: 15.01.09 14:06
Оценка:
На прологе
% первая
doit :-
    process([aaa, bbb, abc, zzz, qqq, qwerty, xyz, zzz]).

process([H | T]) :-
    atom_codes(H, S),
    S = [F | _],
    atom_codes(Fa, [F]),
    append(Fa),
    format('~w~n', [H]),
    told,
    process(T).

process([]).

%%%
% вторая
doit1 :-
    f([
       dir1/file1,
       dir1/dir2/file2,
       dir1/dir3/file3,
       dir4/file4
      ], dir1, Res),
    write(Res).

f([D/D1/_ | T], D, (Fres, [D1 | Dres])) :-
    f(T, D, (Fres, Dres)), !.

f([D/F | T], D, ([F | Fres], Dres)) :-
    f(T, D, (Fres, Dres)), !.

f([_ | T], D, (Fres, Dres)) :-
    f(T, D, (Fres, Dres)), !.

f([], _, ([], [])).


Вывод второй задачи:
?- doit1.
[file1], [dir2, dir3]
true.
prolog
Re[5]: Мастер-класс по ФП
От: D. Mon Великобритания http://thedeemon.livejournal.com
Дата: 15.01.09 15:06
Оценка:
Здравствуйте, BulatZiganshin, Вы писали:

BZ>я может чего-то не понимаю, но о каком обобщении здесь идёт речь?


Обобщение алгоритма сортировки на две подзадачи: в одном случае просто сортировка, в другом — с оставлением только уникальных строк и подсчетом их количества. Например, там могут быть разные функции чтения, записи, сравнения и комбинирования, а функция сортировки — одна общая.

BZ>я как раз и говорю о том, что решение данной задачи не поможет развить фп-стиль программирования.


Тут немного ранее обсуждали, что на слишком маленьких задачах не продемонстрировать ФП-подход к дизайну, чтобы можно было его сравнить с ООП-дизайном. Мне показалось, что данная задача уже дает такую возможность.
Re[6]: Мастер-класс по ФП
От: BulatZiganshin  
Дата: 15.01.09 15:24
Оценка:
Здравствуйте, D. Mon, Вы писали:

BZ>>я может чего-то не понимаю, но о каком обобщении здесь идёт речь?


DM>Обобщение алгоритма сортировки на две подзадачи: в одном случае просто сортировка, в другом — с оставлением только уникальных строк и подсчетом их количества. Например, там могут быть разные функции чтения, записи, сравнения и комбинирования, а функция сортировки — одна общая.


можно конечно, но по-моему это слишком сложно. проще поставить агрегатор после merge

BZ>>я как раз и говорю о том, что решение данной задачи не поможет развить фп-стиль программирования.


DM>Тут немного ранее обсуждали, что на слишком маленьких задачах не продемонстрировать ФП-подход к дизайну, чтобы можно было его сравнить с ООП-дизайном. Мне показалось, что данная задача уже дает такую возможность.


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

кроме того, обсуждать его надо не на примере полного решения маленьких задач, а на разборе именно дизайна крупных задач или библиотек
Люди, я люблю вас! Будьте бдительны!!!
Re[6]: Мастер-класс по ФП
От: Beam Россия  
Дата: 15.01.09 15:24
Оценка:
Здравствуйте, BulatZiganshin, Вы писали:

BZ>ну симпортируйте стандартные Data.List, Control.Monad и прочая


Запустил Выделил необходимые исправления:

import Data.List
import Control.Monad
import Control.Arrow

main = do -- (Лениво) создаём отсортированные подсписки
          sublists <- fmap sort1 getContents
          -- Записываем их в файлы с именами 1..n
          zipWithM_ writeFile (map show [1..]) sublists
          -- Читаем (опять же лениво) содержимое всех ранее созданных файлов
          sublists <- mapM (fmap lines . readFile . show) [1..length sublists]  -- точка перед readFile нужна еще
          -- Сливаем их и записываем результат
          putStr (unlines (mergesort' compare sublists))

sort1 = lines >>> unfoldr (\xs -> guard (xs>[]) >> Just (splitAt 5000 xs)) >>> map (sort>>>unlines)


BZ>это полный пример


Да, я сначала не понял, что работает с stdin stdout

BZ>нет. readFile = openFile >>= hGetContents, т.е. он читает файл так же лениво и безалаберно


Действительно, я не знал. Однако ж, в этом решении все данные грузятся в память и причину я не могу понять.
... << RSDN@Home 1.2.0 alpha 4 rev. 1136>>
Best regards, Буравчик
Re[6]: Мастер-класс по ФП
От: Курилка Россия http://kirya.narod.ru/
Дата: 15.01.09 15:24
Оценка:
Здравствуйте, D. Mon, Вы писали:

DM>Здравствуйте, BulatZiganshin, Вы писали:


BZ>>я может чего-то не понимаю, но о каком обобщении здесь идёт речь?


DM>Обобщение алгоритма сортировки на две подзадачи: в одном случае просто сортировка, в другом — с оставлением только уникальных строк и подсчетом их количества. Например, там могут быть разные функции чтения, записи, сравнения и комбинирования, а функция сортировки — одна общая.


Эээ, как у тебя обобщение даёт из 1 задачи 2?
Re[3]: Мастер-класс по ФП
От: D. Mon Великобритания http://thedeemon.livejournal.com
Дата: 15.01.09 15:54
Оценка:
Здравствуйте, Beam, Вы писали:

B>mergeChains :: [FilePath] -> FilePath -> IO ()


Неплохо, только на файле из 20 млн строк получится нужно одновременно открыть 2000 файлов. ОС позволит? Попарный мердж, как у Булата, тут подойдет лучше.

А теперь самое интересное — пункт 2 (про подсчет уникальных строк). Интересно, как сильно надо будет менять программу для его реализации.
Re[4]: Мастер-класс по ФП
От: Beam Россия  
Дата: 15.01.09 16:49
Оценка:
Здравствуйте, D. Mon, Вы писали:

B>>mergeChains :: [FilePath] -> FilePath -> IO ()

DM>Неплохо, только на файле из 20 млн строк получится нужно одновременно открыть 2000 файлов. ОС позволит? Попарный мердж, как у Булата, тут подойдет лучше.

Ну, все не так плохо. Там же задается maxSize.
Вот расход памяти для разных maxSize, применительно к тестовым файлам.
maxSize = 20000 — 29 Mb
maxSize = 50000 — 61 Mb
maxSize = 200000 — 273 Mb
т.е. используя maxSize = 200 тыс., файл 20 млн. строк даст 10 файлов
Хотя, конечно, когда-нибудь упремся в количество файлов

Попарное слияние увеличивает время работы алгоритма.
Если мы держим только несколько файлов открытыми (не все), то и слияние в данный момент можем сделать только на них. Мы их читаем, потом объединяем в один файл побольше. Но этот файл (который побольше) снова надо объединять с другими, а значит мы его снова должны прочитать. В любом случае, если делать серьезную реализацию, надо учитывать возможность ограничения на количество открытых файлов, а соответственно — делать промежуточное слияние.

Алгоритм Булата практически ничем не отличается. Только он красивее выглядит Алгоритм слияния хоть и "выглядит попарным" (так проще), на самом деле для его работы также открываются все файлы одновременно.

К тому же, на текущий момент, там все данные загружаются в память, а значит вообще временные файлы не нужны.
Надежда была на полную ленивость, но программа получается энергичная. Хотелось бы это исправить.

DM>А теперь самое интересное — пункт 2 (про подсчет уникальных строк). Интересно, как сильно надо будет менять программу для его реализации.


Позже попробую написать. Но принцип, думаю, будет такой:
В mergeChains', там где строка выводится в итоговый файл добавим функцию, которая будет подсчитывать количество повторов последней строки, а затем выводить ее саму и количество в другой файл A2.
... << RSDN@Home 1.2.0 alpha 4 rev. 1136>>
Best regards, Буравчик
Re[13]: Мастер-класс по ФП
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 15.01.09 17:05
Оценка:
Здравствуйте, BulatZiganshin, Вы писали:

BZ>вот пример. представим классическую gui-библиотеку.


Кстати, да. Представим. Как с помощью ФП представлять такие абстрации, как органы управления вроде button, editbox, listbox и пр.? Т.е. как их с нуля реализовать пользуясь только ФП?

Это не сарказм, не подколка. Просто я пока не представляю себе этого.


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[14]: Мастер-класс по ФП
От: BulatZiganshin  
Дата: 15.01.09 18:38
Оценка:
Здравствуйте, eao197, Вы писали:

BZ>>вот пример. представим классическую gui-библиотеку.


E>Кстати, да. Представим. Как с помощью ФП представлять такие абстрации, как органы управления вроде button, editbox, listbox и пр.? Т.е. как их с нуля реализовать пользуясь только ФП?


да так же, как и в любом другом процедурном языке. у вас от 20-летия ООП крыши совсем посносило. вы уже верите, что до ооп не было ни библиотек, ни повторного использования кода
Люди, я люблю вас! Будьте бдительны!!!
Re[15]: Мастер-класс по ФП
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 15.01.09 18:42
Оценка:
Здравствуйте, BulatZiganshin, Вы писали:

E>>Кстати, да. Представим. Как с помощью ФП представлять такие абстрации, как органы управления вроде button, editbox, listbox и пр.? Т.е. как их с нуля реализовать пользуясь только ФП?


BZ>да так же, как и в любом другом процедурном языке. у вас от 20-летия ООП крыши совсем посносило. вы уже верите, что до ооп не было ни библиотек, ни повторного использования кода


Извини, это общие слова. Даже когда писались окна на процедурных языках, все равно там была какая-то эмуляция ООП. И были проблемы с наследованием и переопределением поведения.

А как в ФП с функциями и отсутствием изменяемого состояния все это сделать нормально?


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[15]: Мастер-класс по ФП
От: BulatZiganshin  
Дата: 15.01.09 18:48
Оценка:
ещё одна задача из моей практики. есть список файлов, каждый файл представлен структурой, содержащей поля ext (расширение файла), size (его размер) и другие, нам неинтересные. нужно разбить этот список на группы (солид-блоки) по критериям, описанным строкой вида [Nf][Mb][e], где N и M — некоторые числа. эта запись означает, что каждая группа может содержать не более N файлов, суммарным объёмом не более M байт, с одинаковым расширением. все три части этой строки необязательны — при их отсутствии соответствующий критерий не применяется
Люди, я люблю вас! Будьте бдительны!!!
Re[16]: Мастер-класс по ФП
От: Mr.Cat  
Дата: 15.01.09 19:36
Оценка:
Здравствуйте, BulatZiganshin, Вы писали:
BZ>ещё одна задача из моей практики.

Есть ли дополнительные ограничения на результирующие группы? Например, идет ли речь о разбиении на минимальное число групп? Или критерий оценки решения — элегантность и разумная вычислительная сложность?
Re[17]: Мастер-класс по ФП
От: BulatZiganshin  
Дата: 15.01.09 19:59
Оценка:
Здравствуйте, Mr.Cat, Вы писали:

MC>Здравствуйте, BulatZiganshin, Вы писали:

BZ>>ещё одна задача из моей практики.

MC>Есть ли дополнительные ограничения на результирующие группы? Например, идет ли речь о разбиении на минимальное число групп? Или критерий оценки решения — элегантность и разумная вычислительная сложность?


при разбиении нельзя менять порядок файлов. проводится оно лишь приблизительно (в смысле суммарного объёма файлов в каждой группе)
Люди, я люблю вас! Будьте бдительны!!!
Re: Мастер-класс по ФП
От: VladD2 Российская Империя www.nemerle.org
Дата: 19.01.09 00:24
Оценка: +1 -1
Здравствуйте, BulatZiganshin, Вы писали:

BZ>я часто сталкиваюсь с двумя точками зрения — первая "я знаком с ФП, но оно никакой выгоды в программировании не даёт", и вторая "я знаком с ФП, но так и не научился использовать его для повышения эффективности программирования". собственно, это одно и то же, разница только в том, на кого (ФП или самого человека) возлагается вина за отсутствие результата


Если ты часто сталкиваешся с подобным мнением, то тебе не составит труда дать ссылки на 5-6 примеров такого мнения.
Дай, плиз.

BZ>1) есть список слов. надо записать в файл "a" все слова, начинающиеся на букву 'a', в файл "b" — слова на 'b' и т.д. вплоть до z


Что-то я не пойму... Ты вроде говорил о задачах из реальной жизни, а предлагаешь написать какие-то примитивные примерчики краткость и понятность которых в основном зависят от наличия удобных библиотек.

Вот решение твоей задачи на Немерле в императивной монере:
def words = ["abba", "yield"];
foreach (group in words.GroupBy(w => w[0]))
  File.WriteAllText($"$(group.Key).txt", $"..$group");

Что тут из реального мира?

BZ>2) имеется список имён файлов в архиве:


BZ>dir1\file1

BZ>dir1\dir2\file2
BZ>dir1\dir3\file3
BZ>dir4\file4

BZ>требуется написать функцию, которая по имени каталога возвращает список файлов и подкаталогов в нём, к примеру


BZ>f "dir1" = (["file1"], ["dir2","dir3"])


BZ>a) сложностью O(кол-во файлов)

BZ>б) более эффективную

def getNestsdDirsAndFiles(rootDir)
{
  def dirs  = Directory.GetDirectories(rootDir, "*.*", SearchOption.AllDirectories);
  def files = Directory.GetFiles(rootDir, "*.*", SearchOption.AllDirectories);
  (dirs, files)
}


Эффективнее не придумаешь.

Почти тоже самое можно написать на C# и даже на Яве.

ЗЫ

Что постановка задачи, что примеры просто смешны. Никого и ни в чем они не убедят.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: Мастер-класс по ФП
От: VoidEx  
Дата: 19.01.09 01:12
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>
VD>def getNestsdDirsAndFiles(rootDir)
VD>{
VD>  def dirs  = Directory.GetDirectories(rootDir, "*.*", SearchOption.AllDirectories);
VD>  def files = Directory.GetFiles(rootDir, "*.*", SearchOption.AllDirectories);
VD>  (dirs, files)
VD>}


VD>Эффективнее не придумаешь.


VD>Почти тоже самое можно написать на C# и даже на Яве.


Ух ты! А можно пример, как задать список файлов и директорий, чтоб это вызвать?
Re[3]: Мастер-класс по ФП
От: VladD2 Российская Империя www.nemerle.org
Дата: 19.01.09 10:24
Оценка:
Здравствуйте, VoidEx, Вы писали:

VE>Ух ты! А можно пример, как задать список файлов и директорий, чтоб это вызвать?


Можно говорить по делу и без иносказаний?

Ты не согласен с тем, что задачки детские и не имеющие ничего общего с реальным миром?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: Мастер-класс по ФП
От: VoidEx  
Дата: 19.01.09 11:35
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Ты не согласен с тем, что задачки детские и не имеющие ничего общего с реальным миром?


Ну ты ж решение не привёл, значит не решил детскую задачу
Точнее, решение-то привёл, но своей какой-то задачи, а не поставленной.
Re[5]: Мастер-класс по ФП
От: VladD2 Российская Империя www.nemerle.org
Дата: 19.01.09 12:29
Оценка:
Здравствуйте, VoidEx, Вы писали:

VD>>Ты не согласен с тем, что задачки детские и не имеющие ничего общего с реальным миром?


VE>Ну ты ж решение не привёл, значит не решил детскую задачу


Вообще-то привел, хотя и не обязан был.

VE>Точнее, решение-то привёл, но своей какой-то задачи, а не поставленной.


Я привел решение задачи так как ее понял. То что она не определена строго формально — это не моя вина.

ЗЫ

В общем, опять не по делу говоришь.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[6]: Мастер-класс по ФП
От: VoidEx  
Дата: 19.01.09 12:36
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Я привел решение задачи так как ее понял. То что она не определена строго формально — это не моя вина.


Что? Перечитай условие ещё раз. Дан список имён файлов. Какое именно слово тебе не ясно?
Откуда взялась какая-то директория rootDir? Куда передать исходный список имён файлов и откуда получить результат?
Где решение-то?

VD>Вот решение твоей задачи на Немерле в императивной монере:

VD>def words = ["abba", "yield"];
VD>foreach (group in words.GroupBy(w => w[0]))
VD>  File.WriteAllText($"$(group.Key).txt", $"..$group");


Можно вопрос. Так это императивная монера, или нет?
В своей последней статье это у тебя функциональный подход.
Вы бы уже определились что ли?

Итого:
1-я задача решена кратко в функциональном стиле
2-я задача не решена вообще
Re[2]: Мастер-класс по ФП
От: BulatZiganshin  
Дата: 19.01.09 13:32
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Если ты часто сталкиваешся с подобным мнением, то тебе не составит труда дать ссылки на 5-6 примеров такого мнения.

VD>Дай, плиз.

а почему не 500-600? вот к примеру: http://rsdn.ru/Forum/Default.aspx?mid=3254353
Автор: VladD2
Дата: 19.01.09


VD>Вот решение твоей задачи на Немерле в императивной монере:


тут интересно вспомнить, что это решение тебе не пришло в голову до тех пор, пока я не привёл его на хаскеле. т.е. это как раз пример того, как фп подход помог упростить решение задачи

BZ>>2) имеется список имён файлов в архиве:


BZ>>dir1\file1

BZ>>dir1\dir2\file2
BZ>>dir1\dir3\file3
BZ>>dir4\file4

BZ>>требуется написать функцию, которая по имени каталога возвращает список файлов и подкаталогов в нём, к примеру


BZ>>f "dir1" = (["file1"], ["dir2","dir3"])


BZ>>a) сложностью O(кол-во файлов)

BZ>>б) более эффективную

ты невнимательно прочёл. у нас есть список файлов, содержащихся В АРХИВЕ. и нужна функция, возвращающая спсиок файлов/каталогов определённом каталоге внутри архива. причём в двух вариантах — простом и с построением промежуточной структуры данных для ускорения поиска

далее по тексту можно найти ещё три задачи
Люди, я люблю вас! Будьте бдительны!!!
Re[7]: Мастер-класс по ФП
От: VladD2 Российская Империя www.nemerle.org
Дата: 19.01.09 17:47
Оценка:
Здравствуйте, VoidEx, Вы писали:

VE>Что? Перечитай условие ещё раз. Дан список имён файлов. Какое именно слово тебе не ясно?

VE>Откуда взялась какая-то директория rootDir? Куда передать исходный список имён файлов и откуда получить результат?
VE>Где решение-то?


имеется список имён файлов в архиве...требуется написать функцию, которая по имени каталога возвращает список файлов и подкаталогов в нём, к примеру...


Я так понял речь идет о каталоге. Ну, даже если я понял не верно, все равно задача от это реалистичной и темболе что-то демонстрирующей не становится. Проектирования в ней — 0. Банальная мелкая функция. Опять же количество вопросов только увеличивается. Как, например, понять что что-то файл, а что-то каталог?

VD>>Вот решение твоей задачи на Немерле в императивной монере:

VE>
VD>>def words = ["abba", "yield"];
VD>>foreach (group in words.GroupBy(w => w[0]))
VD>>  File.WriteAllText($"$(group.Key).txt", $"..$group");
VE>


VE>Можно вопрос. Так это императивная монера, или нет?


Цикл видишь? С каких пор в ФП циклы стали. GroupBy — конечно функция. Причем функция высшего порядка. Так что конечно подход можно назвать смешанным, но он точно не чисто функциональный. И таким, смешанным способом задачу решить куда проще чем выпендриваясь в функциональном стиле. Запись в файл сама по себе является императивной операцией. Просто глупо пытаться осуществить ее с помощью функциональных изысков.
формирование же строк производится вообще макросами. Любой хаскель нерно курит по сравнению с ними, так как при большем объеме шума и рядом не будет стоять по скорости.

Итого пример скорее демонстрирует бессмысленность функционального подхода для выполнения императивных операций. И одновременно удобство его же при обработке списков.
Все чему такой пример может научить, так это смотреть на некоторые вещи как на обработку списков. Но стоило ли такую банальную мысль облачать в красивое слово "мастер-класс".

VE>В своей последней статье это у тебя функциональный подход.

VE>Вы бы уже определились что ли?

См. выше.

VE>Итого:

VE>1-я задача решена кратко в функциональном стиле
VE>2-я задача не решена вообще

Если честно, мне вообще их решать было не нужно. В прочем, ты мягко говоря лукавишь.

Еще раз. Основной смысл моих слов заключался в том, что такие примеры ничего ровным счетом не демонстрируют.

Основные проблемы ФП заключаются в том, что чистый ФП не предоставляют средств проектировани более-менее серьезных систем. И если уж и устраивать мастер-классы, то неплохо было бы продемонстрировать новичкам как с помощью одного лишь ФП спроектировать и написать каркас небольшого, но не примитивного приложения.

С удовольствием бы послушал тех кто считает, что он в силах это сделать. Лично я не вижу как это сделать. Я бы воспользовался бы ООП для проектирования каркаса и ФП для реализации его частей.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[7]: Мастер-класс по ФП
От: VladD2 Российская Империя www.nemerle.org
Дата: 19.01.09 17:52
Оценка:
Здравствуйте, VoidEx, Вы писали:

Кстати, ты полез обсуждать последний пример, но пропустил мой первый вопрос.
А мне вот интересно согласен ли ты с автором темы по поводу:

я часто сталкиваюсь с двумя точками зрения — первая "я знаком с ФП, но оно никакой выгоды в программировании не даёт", и вторая "я знаком с ФП, но так и не научился использовать его для повышения эффективности программирования". собственно, это одно и то же, разница только в том, на кого (ФП или самого человека) возлагается вина за отсутствие результата


Я вот с ними ни разу не сталкивался. Я сталкивался с непониманием того, что такое ФП. С заявлениями, что "у меня и так все хорошо и мне хватает Блаба". И с заявлениями о том, что ФП человек не понимает и не хочет или не может его понять.

А так чтобы человек говорил, что понимает ФП но не знает как его применить лично я не сталкивался.

Так вот часто ли ты слышал вопросы в постановке автора темы?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[3]: Мастер-класс по ФП
От: VladD2 Российская Империя www.nemerle.org
Дата: 19.01.09 18:13
Оценка:
Здравствуйте, BulatZiganshin, Вы писали:

BZ>Здравствуйте, VladD2, Вы писали:


VD>>Если ты часто сталкиваешся с подобным мнением, то тебе не составит труда дать ссылки на 5-6 примеров такого мнения.

VD>>Дай, плиз.

BZ>а почему не 500-600?


Дай 600, если сможешь. В общем-то любое количество большее двух уже устроило бы. А то я то и одного такого заявления не слышал. Сдается мне, что кто-то хочет побороться с ветряными мельницами.

BZ>вот к примеру: http://rsdn.ru/Forum/Default.aspx?mid=3254353
Автор: VladD2
Дата: 19.01.09


Дурака из себя строишь?
В прочем, я так и думал, что ссылок у тебя нет. Видимо по тому, что их нет в природе и ты действительно нашел себе мельницу чтобы по воевать.

VD>>Вот решение твоей задачи на Немерле в императивной монере:


BZ>тут интересно вспомнить, что это решение тебе не пришло в голову до тех пор, пока я не привёл его на хаскеле. т.е. это как раз пример того, как фп подход помог упростить решение задачи


Я вообще не читал ветки вглубь. Да и скромнее надо быть.
К тому же не помню, чтобы в хастелое были подобные функции группировки по ключу.

Что же до упрощения, то конечно когда речь идет о задачах преобразования последовательностей, то ФП рулит. Он ведь родом из Лиспа. А у Лиспа название говорит само за себя.

Вот только ты ведь наверно заметил, что половина решения использует ФП, а вторая старый добрый императивный подход который в данном случае куда удобнее ФП. Ведь задача записи в файл императивна по сути.

Так что пример отлично демонстрирует, что один ФП жив не будешь. И что нужно грамотно выбирать что нужно делать в функциональном стиле, а что в императивном.

Но претензия тут у меня в другом. На мой взгляд задачи слишком малы чтобы говорить о проектировании. А именно это не понятно большинству императивных программистов столкнувшихся с ФП.

BZ>ты невнимательно прочёл. у нас есть список файлов, содержащихся В АРХИВЕ. и нужна функция, возвращающая спсиок файлов/каталогов определённом каталоге внутри архива. причём в двух вариантах — простом и с построением промежуточной структуры данных для ускорения поиска


ОК. Мне тут уже намекнули на это. Согласен. Наверно я не верно понял слово "архив". Почему-то подумал, что ты назвал так каталог.

Но претензия к задаче все та же. Она примитивна. Это тактические задачи. На них нельзя продемонстрировать приемущества ФП. В любом случае задача решится написанием более-менее приемлемой промежуточной абстракции (в том или ином виде). Тут подойдут и императивные итераторы, и функциональная рекурсия.

BZ>далее по тексту можно найти ещё три задачи


Не интересны они. Это упражнения на тему "как бы я реализовал ту или иную утилитарную функцию".

Я для себя уже давно понял, что ФП сам по себе панацеей не является. Это отличный взгляд декомпозицию алгоритмов. Но кроме алгоритмов в современных программах есть еще кое что — архитектура. И это кое что ФП упростить не в силах. Вот если бы ты показал примеры где ФП давало бы архитектурные преимущества, то я бы лично поблагодарил тебя от всей души.

Пока что ФП даем мне следующие приемущества:
1. Декомпозиция на микроскопическом уровне. Это позволяет то, что я раньше писал циклами (от безысходности) поместить в маленькие функции принимающие "уточняющие куски кода" (т.е. в Фвп).
2. Алгебраические типы данных позволяют упростить задачи анализа и/или преобрзования иерархий объектов.
3. Неизменяемость позволяет упростить отладку и распаралеливание/параллельный доступ.

Все это я бы назвал тактическими преимуществами. Они очень приятны и важны. Но для полного превосходства нужно иметь стратегические преимущества — упрощение дизайна приложений. Если же стратегических приемуществ достичь нельзя, то надо совмещать ФП, с чем-то еще. На сегодня это ООП, КОП и МП.

Если я не прав, то хотелось бы услышать развернутое опровержение. Только плиз, без догм.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[8]: Мастер-класс по ФП
От: VoidEx  
Дата: 19.01.09 18:19
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Цикл видишь? С каких пор в ФП циклы стали. GroupBy — конечно функция. Причем функция высшего порядка. Так что конечно подход можно назвать смешанным, но он точно не чисто функциональный. И таким, смешанным способом задачу решить куда проще чем выпендриваясь в функциональном стиле. Запись в файл сама по себе является императивной операцией. Просто глупо пытаться осуществить ее с помощью функциональных изысков.

Хм, чем foreach, называемый тобой в данном случае циклом, проще или сложнее map(M)?
mapM_ writeToFile lst

Это что, императивно? А между прочим, пишет в файл.

VD>формирование же строк производится вообще макросами. Любой хаскель нерно курит по сравнению с ними, так как при большем объеме шума и рядом не будет стоять по скорости.

Это просто хвастовство, или какое-то отношение к теме имеет?

VE>>В своей последней статье это у тебя функциональный подход.

VE>>Вы бы уже определились что ли?

VD>См. выше.

Там пространные какие-то рассуждения. Ты скажи одним словом, функциональный это подход или императивный?
А то у тебя от контекста зависит по ходу. Теперь вот это вообще смешанный подход оказался.
Re[8]: Мастер-класс по ФП
От: VoidEx  
Дата: 19.01.09 18:24
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Кстати, ты полез обсуждать последний пример, но пропустил мой первый вопрос.

VD>А мне вот интересно согласен ли ты с автором темы по поводу:
VD>

VD>я часто сталкиваюсь с двумя точками зрения — первая "я знаком с ФП, но оно никакой выгоды в программировании не даёт", и вторая "я знаком с ФП, но так и не научился использовать его для повышения эффективности программирования". собственно, это одно и то же, разница только в том, на кого (ФП или самого человека) возлагается вина за отсутствие результата

Не размышлял на эту тему, так что не могу сказать, согласен или нет. Но точка зрения такая зачастую присутствует, хотя это не относится только к ФП. Это вообще нередкость — думать, что "знакомы с (подставь нужное), но никакой выгоды нет".

VD>Я вот с ними ни разу не сталкивался. Я сталкивался с непониманием того, что такое ФП. С заявлениями, что "у меня и так все хорошо и мне хватает Блаба". И с заявлениями о том, что ФП человек не понимает и не хочет или не может его понять.

Куда хуже, когда человек думает, что всё понял.

VD>А так чтобы человек говорил, что понимает ФП но не знает как его применить лично я не сталкивался.

Не сочти за оскорбление, но

Лично я не вижу как это сделать. Я бы воспользовался бы ООП для проектирования каркаса и ФП для реализации его частей.


VD>Так вот часто ли ты слышал вопросы в постановке автора темы?

Нет
Re[4]: Мастер-класс по ФП
От: VoidEx  
Дата: 19.01.09 18:32
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Так что пример отлично демонстрирует, что один ФП жив не будешь. И что нужно грамотно выбирать что нужно делать в функциональном стиле, а что в императивном.

Задача исходного поста была показать, как функциональный подход может дать преимущества.

По поводу проектирования, я тут
Автор: VoidEx
Дата: 07.01.09
приводил решение в функциональном стиле, которое элементарно параллелится. В императивном предлагалось менять какие-то общие переменные, отсюда все вытекающие.
Ты, конечно, можешь взять queue и назвать всё в итоге императивщиной, но это, очевидно, не так.

P.S. Хочу попросить тебя о маленьком одолжении: не приплетать макросы (которые только поднимут очередной холивар, в котором ты обвинишь окружающих) и не извращать слова остальных участников (а то уже началось, будто ФП предлагают панацеей, хотя нигде этого не сказано). Было бы чудно.

P.P.S. Почему ссылка и последующее слово сливаются? Там точно есть пробел
Re[8]: Мастер-класс по ФП
От: Gajdalager Украина  
Дата: 19.01.09 20:14
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>>>Вот решение твоей задачи на Немерле в императивной монере:

VE>>
VD>>>def words = ["abba", "yield"];
VD>>>foreach (group in words.GroupBy(w => w[0]))
VD>>>  File.WriteAllText($"$(group.Key).txt", $"..$group");
VE>>


VE>>Можно вопрос. Так это императивная монера, или нет?


VD>Цикл видишь? С каких пор в ФП циклы стали.

Если придраться к терминам, то это не совсем цикл, а внутренний итератор, реализированный как конструкция языка... А это скорее ФП чем не ФП
<< RSDN@Home 1.2.0 alpha 4 rev. 1128>>
Сейчас играет In Extremo — Tannhuser
Re[9]: Мастер-класс по ФП
От: Курилка Россия http://kirya.narod.ru/
Дата: 19.01.09 20:34
Оценка:
Здравствуйте, Gajdalager, Вы писали:

G>Если придраться к терминам, то это не совсем цикл, а внутренний итератор, реализированный как конструкция языка... А это скорее ФП чем не ФП


Ммм, разве итераторы стали функциональными?
Вон на хаскеле их чтот ни разу не видел, наверное он нефункциональный?
Re[10]: Мастер-класс по ФП
От: VoidEx  
Дата: 19.01.09 21:04
Оценка: -1
Здравствуйте, Курилка, Вы писали:

К>Ммм, разве итераторы стали функциональными?

К>Вон на хаскеле их чтот ни разу не видел, наверное он нефункциональный?
Как это, а вот это — [a] — что?
На императивных ленивые списки (потоки, последовательности, етц.) эмулируется итераторами, так что это очень даже функциональная фишка.
Re[4]: Мастер-класс по ФП
От: Mr.Cat  
Дата: 19.01.09 23:46
Оценка:
Здравствуйте, VladD2, Вы писали:
VD>Все это я бы назвал тактическими преимуществами. Они очень приятны и важны. Но для полного превосходства нужно иметь стратегические преимущества — упрощение дизайна приложений.

А на мой взгляд упрощение дизайна в некоторых случаях достигается. Мне вот тут на днях надо было простенький веб-интерфейс сделать, и я решил сделать его на scheme. PLT-шный фреймворк показался удачным — и когда я решил узнать про него побольше, оказалось, это т.н. основанный на продолжениях (continuations based) веб-фреймворк. Фишка таких фреймворков в том, что вместо манипулирования состоянием, программист манипулирует континуацией. Т.е., например, отправляя юзеру форму, программист указывает функцию, которая получит данные формы. Получается вполне удобно.
Re[10]: Мастер-класс по ФП
От: Gajdalager Украина  
Дата: 20.01.09 06:27
Оценка:
Здравствуйте, Курилка, Вы писали:

К>Здравствуйте, Gajdalager, Вы писали:


G>>Если придраться к терминам, то это не совсем цикл, а внутренний итератор, реализированный как конструкция языка... А это скорее ФП чем не ФП


К>Ммм, разве итераторы стали функциональными?

К>Вон на хаскеле их чтот ни разу не видел, наверное он нефункциональный?

ИМХО внутренний итератор как раз привносит функциональную парадигму, т.к. являет собой использование ФВП (в не-ФП языках эмулированных с помощью паттерна Command). По духу вполне функционально. И ф-я foreach — разве это не внутренний итератор (если использовать терминологию GoF)?
Ну а внешний итератор, то который next/hasNext функциональным вряд ли можно назвать, конечно
<< RSDN@Home 1.2.0 alpha 4 rev. 1128>>
Сейчас играет In Extremo — Singapur
Re[9]: Мастер-класс по ФП
От: Трурль  
Дата: 20.01.09 13:02
Оценка:
Здравствуйте, VoidEx, Вы писали:

VE>
VE>mapM_ writeToFile lst
VE>

VE>Это что, императивно? А между прочим, пишет в файл.

Раз пишет в файл, значит императивно. По определению.
Re[10]: Мастер-класс по ФП
От: Gajdalager Украина  
Дата: 20.01.09 13:20
Оценка:
Здравствуйте, Трурль, Вы писали:

Т>Здравствуйте, VoidEx, Вы писали:


Т>Раз пишет в файл, значит императивно. По определению.


Определение в студию Отсутствие side-эффектов не обязательно для программирования в функциональном стиле.
<< RSDN@Home 1.2.0 alpha 4 rev. 1128>>
Сейчас играет In Extremo — Nur Ihr Allein
Re[10]: Мастер-класс по ФП
От: palm mute  
Дата: 20.01.09 13:38
Оценка:
Здравствуйте, Трурль, Вы писали:

VE>>
VE>>mapM_ writeToFile lst
VE>>

VE>>Это что, императивно? А между прочим, пишет в файл.

Т>Раз пишет в файл, значит императивно. По определению.


Согласен. А здесь — у нас только функция evalIO_ императивна, или writeAll тоже?
{-# LANGUAGE GADTs  #-}

data IO_ a where
    WriteToFile :: String -> IO_ ()
    Sequence :: [IO_ ()] -> IO_ ()
    -- etc

writeAll xs = Sequence (map WriteToFile xs)


evalIO_ :: IO_ a -> IO a
evalIO_ = ...


Вообще спор старый как мир, и по большому счету бессмысленный. Разница между императивным и декларативным — в голове наблюдателя. А наличие какой-то системы эффектов (одним из вариантов реализации которой являются монады — заметьте, я не утверждаю, что самым лучшим) — объективный факт, все остальное — спекуляции.
Re[10]: Мастер-класс по ФП
От: VoidEx  
Дата: 20.01.09 14:17
Оценка:
Здравствуйте, Трурль, Вы писали:

Т>Раз пишет в файл, значит императивно. По определению.

Забавно
Re[10]: Мастер-класс по ФП
От: Mr.Cat  
Дата: 20.01.09 14:54
Оценка:
Здравствуйте, Трурль, Вы писали:
Т>Раз пишет в файл, значит императивно. По определению.

А вот и нет. На вход мы получаем пустой файл, на выходе получаем новый — с данными. То, что гадкая императивная ОСь записывает новенький файл на место старого — уже не наша забота.
Re[11]: Мастер-класс по ФП
От: palm mute  
Дата: 20.01.09 14:57
Оценка:
Здравствуйте, Mr.Cat, Вы писали:

MC>А вот и нет. На вход мы получаем пустой файл, на выходе получаем новый — с данными. То, что гадкая императивная ОСь записывает новенький файл на место старого — уже не наша забота.


Запись файла — это наблюдаемый побочный эффект, причем наблюдаемый не только из гадкой императивной оси, но из нашей непорочно чистой программы. Повторюсь, весь фокус — в типизации эффектов.
Re[12]: Мастер-класс по ФП
От: Gajdalager Украина  
Дата: 20.01.09 15:39
Оценка:
Здравствуйте, palm mute, Вы писали:

PM>Здравствуйте, Mr.Cat, Вы писали:


MC>>А вот и нет. На вход мы получаем пустой файл, на выходе получаем новый — с данными. То, что гадкая императивная ОСь записывает новенький файл на место старого — уже не наша забота.


PM>Запись файла — это наблюдаемый побочный эффект, причем наблюдаемый не только из гадкой императивной оси, но из нашей непорочно чистой программы. Повторюсь, весь фокус — в типизации эффектов.


Осторожно, сейчас вы опять с темы съедете Речь шла не о pure-functional и не о отсутствии side-effects, а о программировании в функциональной монере (с). Манера программирования всё же далека от теоретических изысков и подразумевает преимущественное использование неких практик — к ним чаще всего относят использование ФВП, замыкания, иммутабельные структуры и отсутствие side-effects. Однако это не значит, что если в программе есть хоть один side-effect, то она императивна. Опять таки, это спор о терминах, но!
def words = ["abba", "yield"];
foreach (group in words.GroupBy(w => w[0]))
  File.WriteAllText($"$(group.Key).txt", $"..$group");

Я бы назвал эту программу скорее функциональной чем императивной. Есть у меня чёткое ощущение, что внутренний итератор был заимствован из функционального мира.
<< RSDN@Home 1.2.0 alpha 4 rev. 1128>>
Сейчас играет In Extremo — Mein Kind
Re[13]: Мастер-класс по ФП
От: palm mute  
Дата: 20.01.09 15:49
Оценка: 1 (1) +2
Здравствуйте, Gajdalager, Вы писали:

G>Я бы назвал эту программу скорее функциональной чем императивной. Есть у меня чёткое ощущение, что внутренний итератор был заимствован из функционального мира.

Подобные споры возникают с завидной регулярностью именно из-за того, что ощущения у всех разные, и разница между императивным и декларативным находится в философской плоскости. А оффтопик в этой ветке начался значительно раньше моего сообщения.
Re[2]: Мастер-класс по ФП
От: Beam Россия  
Дата: 21.01.09 21:53
Оценка: 10 (1)
Переделал я свой код. Вот что получилось.
Теперь у нас есть bigSort, которая не привязана к файлам и может сортировать большие списки (в условиях ограничений памяти).
a |> f = f a

-- сортирует длинный список
-- разбивает список на последовательные блоки, сортирует их, записывает во временные файлы, затем файлы читает и объединяет
bigSort :: (Read a, Ord a, Show a) => [a] -> IO [a]
bigSort xs = xs |> breakUp 10000 |> map sort |> writeChains >>= readChains >>= (merge >>> return)

-- сохраняет блоки во временные файлы, возвращает имена файлов, куда записал
writeChains chains = do
  let tempFiles = [1..] |> map show |> map ("temp"++)
  let writeChain' (filename, xs) = xs |> map show |> unlines |> writeFile filename >> return filename
  mapM writeChain' (zip tempFiles chains)

-- читает блоки из временных файлов
readChains files = do
  let readChain' file = readFile file >>= (lines >>> map read >>> return)
  mapM readChain' files

-- разбивает список на части размером size
breakUp :: Int -> [a] -> [[a]]
breakUp size xs = let takeBlock ps = guard (null ps |> not) >> Just (splitAt size ps) 
                  in unfoldr takeBlock xs

-- объединяет несколько упорядоченных списков в один упорядоченный список
merge :: (Ord a) => [[a]] -> [a]
merge [] = []
merge ss = foldl1 mergePair ss

mergePair [] ss = ss
mergePair ss [] = ss
mergePair (s1:ss1) (s2:ss2) | s1 <= s2  = s1 : mergePair ss1 (s2:ss2)
                            | otherwise = s2 : mergePair (s1:ss1) ss2


DM>1. Есть большой текстовый файл A (несколько гигов, заведомо не помещается в память), нужно сделать текстовый файл A1, содержащий все строки из А, но отсортированные в алфавитном порядке. Ограничение по памяти — 256 МБ.


main = do 
  args <- getArgs
  let src = head args
  let dst = src ++ "_sorted"
  readFile src >>= (lines >>> bigSort) >>= (unlines >>> writeFile dst)


DM>2. Изменить первую программу так, чтобы будучи запущена с ключом -u она создавала файл А2, содержащий все уникальные строки из А в алфавитном порядке и количество повторений для каждой строки.


main2 = do
  args <- getArgs
  let src = head args
  let dst = src ++ "_sorted"
  let dup = src ++ "_dup"
  let needDup = any (=="-u") args

  sortedData <- readFile src >>= (lines >>> bigSort)

  hdst <- openFile dst WriteMode
  hdup <- openFile dup WriteMode

  -- количество одинаковых строк и сами эти строки
  let gs = [(length g, head g) | g <- group sortedData]
  -- пишем параллельно в sorted и dup
  let writeGroup (count, str) = do
        replicateM_ count (hPutStrLn hdst str) 
        if needDup then hPutStrLn hdup (show count ++ ": " ++ str) else return ()
  mapM_ writeGroup [(length g, head g) | g <- group sortedData]
 
  hClose hdup
  hClose hdst


Не очень красиво, зато укладываемся в ограничения
... << RSDN@Home 1.2.0 alpha 4 rev. 1136>>
Best regards, Буравчик
Re[2]: Мастер-класс по ФП
От: Beam Россия  
Дата: 21.01.09 22:58
Оценка:
Переделал я свой код. Вот что получилось.
Теперь у нас есть bigSort, которая не привязана к файлам и может сортировать большие списки (в условиях ограничений памяти).
a |> f = f a

-- сортирует длинный список
-- разбивает список на последовательные блоки, сортирует их, записывает во временные файлы, затем файлы читает и объединяет
bigSort :: (Read a, Ord a, Show a) => [a] -> IO [a]
bigSort xs = xs |> breakUp 10000 |> map sort |> writeChains >>= readChains >>= (merge >>> return)

-- сохраняет блоки во временные файлы, возвращает имена файлов, куда записал
writeChains chains = do
  let tempFiles = [1..] |> map show |> map ("temp"++)
  let writeChain' (filename, xs) = xs |> map show |> unlines |> writeFile filename >> return filename
  mapM writeChain' (zip tempFiles chains)

-- читает блоки из временных файлов
readChains files = do
  let readChain' file = readFile file >>= (lines >>> map read >>> return)
  mapM readChain' files

-- разбивает список на части размером size
breakUp :: Int -> [a] -> [[a]]
breakUp size xs = let takeBlock ps = guard (null ps |> not) >> Just (splitAt size ps) 
                  in unfoldr takeBlock xs

-- объединяет несколько упорядоченных списков в один упорядоченный список
merge :: (Ord a) => [[a]] -> [a]
merge [] = []
merge ss = foldl1 mergePair ss

mergePair [] ss = ss
mergePair ss [] = ss
mergePair (s1:ss1) (s2:ss2) | s1 <= s2  = s1 : mergePair ss1 (s2:ss2)
                            | otherwise = s2 : mergePair (s1:ss1) ss2


DM>1. Есть большой текстовый файл A (несколько гигов, заведомо не помещается в память), нужно сделать текстовый файл A1, содержащий все строки из А, но отсортированные в алфавитном порядке. Ограничение по памяти — 256 МБ.


main = do 
  args <- getArgs
  let src = head args
  let dst = src ++ "_sorted"
  readFile src >>= (lines >>> bigSort) >>= (unlines >>> writeFile dst)


DM>2. Изменить первую программу так, чтобы будучи запущена с ключом -u она создавала файл А2, содержащий все уникальные строки из А в алфавитном порядке и количество повторений для каждой строки.


main2 = do
  args <- getArgs
  let src = head args
  let dst = src ++ "_sorted"
  let dup = src ++ "_dup"
  let needDup = any (=="-u") args

  sortedData <- readFile src >>= (lines >>> bigSort)

  hdst <- openFile dst WriteMode
  hdup <- openFile dup WriteMode

  -- количество одинаковых строк и сами эти строки
  let gs = [(length g, head g) | g <- group sortedData]
  -- пишем параллельно в sorted и dup
  let writeGroup (count, str) = do
        replicateM_ count (hPutStrLn hdst str) 
        if needDup then hPutStrLn hdup (show count ++ ": " ++ str) else return ()
  mapM_ writeGroup [(length g, head g) | g <- group sortedData]
 
  hClose hdup
  hClose hdst


Не очень красиво, зато укладываемся в ограничения
... << RSDN@Home 1.2.0 alpha 4 rev. 1136>>
Best regards, Буравчик
Re[4]: Мастер-класс по ФП
От: Beam Россия  
Дата: 21.01.09 22:58
Оценка: 25 (2)
Здравствуйте, BulatZiganshin, Вы писали:

BZ>если ограничивать число строк, а не объём занимаемой памяти — то нормально

main = do -- (Лениво) создаём отсортированные подсписки
          sublists <- fmap sort1 getContents
          -- Записываем их в файлы с именами 1..n
          zipWithM_ writeFile (map show [1..]) sublists
          -- Читаем (опять же лениво) содержимое всех ранее созданных файлов
          sublists <- mapM (fmap lines readFile.show) [1..length sublists]
          -- Сливаем их и записываем результат
          putStr (unlines (mergesort' compare sublists)


BZ>на самом деле, даже если я ни в чём не ошибся — этот код будет держать все данные в памяти до победного. extra points — за изящное решение этой маааленькой проблемы


Я, вроде, разобрался в чем тут проблемы — все дело в length sublists. Из-за этого пропадает ленивость.
Если же переписать вот так, то все будет работать:
main = do -- (Лениво) создаём отсортированные подсписки
          sublists <- fmap sort1 getContents
          -- Записываем их в файлы с именами 1..n
          count <- fmap length $ zipWithM writeFile (map show [1..]) sublists
          -- Читаем (опять же лениво) содержимое всех ранее созданных файлов
          sublists <- mapM (fmap lines readFile.show) [1..count]
          -- Сливаем их и записываем результат
          putStr (unlines (mergesort' compare sublists)


Всё таки для Haskell ограничения по памяти противопоказаны
... << RSDN@Home 1.2.0 alpha 4 rev. 1136>>
Best regards, Буравчик
Re[5]: Мастер-класс по ФП
От: BulatZiganshin  
Дата: 21.01.09 23:14
Оценка:
Здравствуйте, Beam, Вы писали:

BZ>>на самом деле, даже если я ни в чём не ошибся — этот код будет держать все данные в памяти до победного. extra points — за изящное решение этой маааленькой проблемы


B>Я, вроде, разобрался в чем тут проблемы — все дело в length sublists. Из-за этого пропадает ленивость.

B>Если же переписать вот так, то все будет работать:

снимаю шляпу. до этого я не додумался
Люди, я люблю вас! Будьте бдительны!!!
Re[16]: Мастер-класс по ФП
От: D. Mon Великобритания http://thedeemon.livejournal.com
Дата: 22.01.09 14:37
Оценка:
Здравствуйте, BulatZiganshin, Вы писали:

BZ>ещё одна задача из моей практики. есть список файлов, каждый файл представлен структурой, содержащей поля ext (расширение файла), size (его размер) и другие, нам неинтересные. нужно разбить этот список на группы (солид-блоки) по критериям, описанным строкой вида [Nf][Mb][e], где N и M — некоторые числа. эта запись означает, что каждая группа может содержать не более N файлов, суммарным объёмом не более M байт, с одинаковым расширением. все три части этой строки необязательны — при их отсутствии соответствующий критерий не применяется


Окамл:
(*базовый вещи *)
open ExtString;;
let (|>) x f = f x;;
let (>>) f g x = g (f x);;

(* структура с информацией о файле *)
type file_t = { name : string; size : int; ext : string };;

(* структура-аккумулятор с текущим блоком файлов *)
type accum_t = { files : file_t list; num : int; totalsize : int};;
let new_acc = { files = []; num = 0; totalsize = 0 };; (* пустой блок *)
let accumulate acc file =
  { files = file::acc.files; num = acc.num + 1; totalsize = acc.totalsize + file.size };;

(* разбор строки параметров *)
let parse_param_string str =
  let numparam ch =
    let rec loop i lst =
      if i<0 then lst else
      if str.[i]>='0' && str.[i]<='9' then loop (i-1) (str.[i] :: lst) else lst  in
    try Some (loop (String.index str ch - 1) [] |> String.implode |> int_of_string)
    with Not_found -> None  in
  numparam 'f', numparam 'b', String.contains str 'e';;

(* основная функция. из списка файлов делает список списков файлов согласно параметрам из строки *)
let make_blocks param_str file_list =
  if param_str = "" then [file_list] else (* простая оптимизация, можно и без нее *)
  let num_opt, size_opt, ext_opt = parse_param_string param_str in (* разбираем параметры *)
  (* создаем предикаты, говорящие можно ли поместить файл в текущий блок *)
  let always = (fun acc file -> true) in
  let size_pred = Option.map_default (fun size_limit -> (fun a f-> a.totalsize + f.size <= size_limit)) always size_opt
  and ext_pred = if ext_opt then (fun a f -> if a.num > 0 then (List.hd a.files).ext = f.ext else true) else always
  and num_pred =  Option.map_default (fun num_limit  -> (fun a f-> a.num + 1 <= num_limit)) always num_opt in 
  (* можно, если все три предиката не возражают *)
  let can_accum = (fun acc file -> List.for_all (fun pred-> pred acc file) [ext_pred; size_pred; num_pred]) in
  (* обработка одного файла: *)
  let process (acc, res_list) file =
    if can_accum acc file then accumulate acc file, res_list
    else accumulate new_acc file, (acc::res_list)  in  
  (* обработка всего списка: собираем новый список fold'ом, разворачиваем и убираем лишнее rev_map'ом *)
  let acc, reslst = List.fold_left process (new_acc, []) file_list in
  acc::reslst |> List.rev_map (fun acc-> List.rev acc.files);;


Пример работы:
let allfiles = [
  { name = "a"; size = 10; ext = "jpg" };
  { name = "b"; size = 20; ext = "jpg" };
  { name = "c"; size = 40; ext = "bmp" };
  { name = "d"; size = 35; ext = "jpg" };
  { name = "e"; size = 10; ext = "bmp" };
  { name = "f"; size = 60; ext = "gif" };
  { name = "g"; size = 45; ext = "gif" };
  { name = "h"; size = 20; ext = "gif" };
  { name = "i"; size = 15; ext = "jpg" };
  { name = "j"; size = 60; ext = "gif" };
  { name = "k"; size = 25; ext = "jpg" };
  { name = "l"; size = 20; ext = "gif" };
];; 
 
let show_file file =
  Printf.sprintf "(%s.%s %d)" file.name file.ext file.size;;

let show =
  List.map (List.map show_file >> String.join ", ") >> String.join "\n";;   
      
make_blocks "4f105b" allfiles |> show |> print_endline;;

(*выводит:
(a.jpg 10), (b.jpg 20), (c.bmp 40), (d.jpg 35)
(e.bmp 10), (f.gif 60)
(g.gif 45), (h.gif 20), (i.jpg 15)
(j.gif 60), (k.jpg 25), (l.gif 20)
*)

make_blocks "e" allfiles |> show |> print_endline;;

(*выводит:
(a.jpg 10), (b.jpg 20)
(c.bmp 40)
(d.jpg 35)
(e.bmp 10)
(f.gif 60), (g.gif 45), (h.gif 20)
(i.jpg 15)
(j.gif 60)
(k.jpg 25)
(l.gif 20)
*)


Использованы только иммутабельные структуры и чистые функции. Вполне возможно, что императивный вариант был бы проще.
Re[17]: Мастер-класс по ФП
От: BulatZiganshin  
Дата: 22.01.09 16:34
Оценка:
Здравствуйте, D. Mon, Вы писали:

DM>Использованы только иммутабельные структуры и чистые функции.


отсутствие побочных эффектов, конечно, хорошо, так как облегчает отладку, но на мой взгляд это ещё не максимум того, что можно выжать из фп подхода. для меня фп стиль — это поток преобразований входных данных в выходные, в идеале без всяких вспомогательных переменных

в данном случае это может быть поток преобразования от строки критериев к *функции* разбиения на подсписки. и состоять он может из таких шагов:

1) разбить строку на отдельные критерии
2) преобразовать каждый из них в функцию разбиения на [ленивые] подсписки по одному этому критерию
3) скомбинировать эти функции в итоговую

и вообще задача тренинга, как я её себе ставил — помочь перейти от мышления, ориентированного на манипуляцию данными, к мышлению, ориентированному на манипуляцию функциями

пробуйте
Люди, я люблю вас! Будьте бдительны!!!
Re[5]: Мастер-класс по ФП
От: BulatZiganshin  
Дата: 22.01.09 16:57
Оценка:
Здравствуйте, Beam, Вы писали:

B>Я, вроде, разобрался в чем тут проблемы — все дело в length sublists. Из-за этого пропадает ленивость.


кинул в cafe результат нашего творчества
Люди, я люблю вас! Будьте бдительны!!!
Re[18]: Мастер-класс по ФП
От: D. Mon Великобритания http://thedeemon.livejournal.com
Дата: 23.01.09 04:45
Оценка:
Здравствуйте, BulatZiganshin, Вы писали:

BZ>2) преобразовать каждый из них в функцию разбиения на [ленивые] подсписки по одному этому критерию

BZ>3) скомбинировать эти функции в итоговую

Хм, если они будут разбивать независимо друг от друга, получится плохо:
Пусть 1-я функция разбивает по количеству файлов, а вторая — по суммарному размеру. Тогда их разбиения могут выглядеть так:
1) 11112222333344445555
2) 11222233334444555567
Что будет результатом комбинирования?
Re[19]: Мастер-класс по ФП
От: BulatZiganshin  
Дата: 23.01.09 09:41
Оценка:
Здравствуйте, D. Mon, Вы писали:

DM>Пусть 1-я функция разбивает по количеству файлов, а вторая — по суммарному размеру. Тогда их разбиения могут выглядеть так:

DM>1) 11112222333344445555
DM>2) 11222233334444555567
DM>Что будет результатом комбинирования?

подумай. я тебе по секрету скажу, что у меня так и работает. ключевое слово — ленивые списки
Люди, я люблю вас! Будьте бдительны!!!
Re[20]: Мастер-класс по ФП
От: D. Mon Великобритания http://thedeemon.livejournal.com
Дата: 23.01.09 09:48
Оценка:
Ленивые списки — подход к реализации. Если результатом комбинирования
11112222333344445555
11222233334444555567
будет
11223344556677... (пересечение)
То это явно неверный ответ. А если не пересечение, то пойду думать..
Re[21]: Мастер-класс по ФП
От: BulatZiganshin  
Дата: 23.01.09 10:22
Оценка:
Здравствуйте, D. Mon, Вы писали:

DM>Ленивые списки — подход к реализации.


ленивые списки — это другая парадигма программирования. к примеру, минимум в списке можно узнать как head.sort . при этом время вычисления будет O(N) — нет нужды вычислять остаток отсортированного списка, так как он всё равно не используется
Люди, я люблю вас! Будьте бдительны!!!
Re[22]: Мастер-класс по ФП
От: D. Mon Великобритания http://thedeemon.livejournal.com
Дата: 24.01.09 12:53
Оценка:
Б> к примеру, минимум в списке можно узнать как head.sort . при этом время вычисления будет O(N)

Только, если функция сортировки достаточно ленивая. Одних ленивых списков недостаточно.

Сделал решение на ленивых списках. Из каждого компонента строки-параметра получается функция, которая берет ленивый список файлов и возвращает в виде ленивого списка файлов его начало — столько, сколько удовлетворяют критерию помещения в один блок (будь то количество, размер или что-то еще). Комбинация этих функций строится путем простой композиции. Результат композиции — функция, возвращающая столько файлов из начала данного ей списка, сколько удовлетворяют всем критериям. Все происходит лениво, лишние файлы не обрабатываются. Используя такую функцию формирования блока, главная функция откусывает от исходного списка файлов по блоку до тех пор, пока тот не исчерпается.

(* определение файла и функция парсинга параметров те же, их не привожу*)

(* универсальная функция выбора одного блока из начала списка файлов по критериям, заданным функциями-параметрами *)
(* принимает ленивый список файлов, возвращает тоже ленивый список файлов *)
(* ленивый список определен как type 'a llist = Nil | Cons of 'a * 'a llist Lazy.t;; *)
let split_by accfun pred init file_llist =
  let rec split acc = function
  | Nil -> Nil
  | Cons(h,t)-> let acc' = accfun acc h in 
                if pred acc' then Cons(h, lazy(split acc' (Lazy.force t))) else Nil in
  split init file_llist;;              
                
(* функции формирования блока, полученные путем уточнения предыдущей *)
let split_by_size size_limit = split_by (fun sz h-> sz + h.size) (fun sz-> sz <= size_limit) 0;;  
let split_by_num num_limit = split_by (fun n h-> n+1) (fun n-> n <= num_limit) 0;;
let split_by_ext = function
  | Nil -> Nil
  | Cons(h,t) -> Cons(h, lazy(split_by (fun _ f -> f.ext) ((=) h.ext) "" (Lazy.force t)));;
      
(* главная функция, интерфейс не менялся: превращает список в файлов в список списков *)
let make_blocks2 param_str =
  let num_opt, size_opt, ext_opt = parse_param_string param_str in (* разбираем параметры *)
  let split = (* строим одну комбинированную функцию получения блока как композицию *)
    [Option.map split_by_num num_opt; (* функций, определенных параметрами *)
     Option.map split_by_size size_opt;
     if ext_opt then Some split_by_ext else None] 
    |> List.fold_left (fun spt so-> Option.map_default ((>>) spt) spt so) Std.identity in                           
  let rec loop blocks flist = (* формируем список блоков *)
    let block = split flist in  let len = length block in 
    if len=0 then blocks else loop (to_list block :: blocks) (drop len flist)  in  
  of_list >> loop [] >> List.rev;;

Результат работы тот же, что раньше.

Использовал самописные ленивые списки:
type 'a llist = Nil | Cons of 'a * 'a llist Lazy.t;;  
          
let rec of_list = function
  | [] -> Nil
  | h::t -> Cons(h, lazy (of_list t));;

let rec to_list = function
  | Nil -> []
  | Cons(h,t) -> h :: to_list (Lazy.force t);;

let rec take n lst =
  if n = 0 then Nil else
  match lst with
  | Nil -> Nil
  | Cons(h,t) -> Cons(h, lazy(take (n-1) (Lazy.force t)));;  

let rec drop n lst =
  if n = 0 then lst else
  match lst with
  | Nil -> Nil
  | Cons(h,t) -> drop (n-1) (Lazy.force t);;

let rec iter f = function
  | Nil -> ()
  | Cons(h, t) -> f h; iter f (Lazy.force t);;

let rec filter p = function
  | Nil -> Nil
  | Cons(h,t)-> if p h then Cons(h, lazy (filter p (Lazy.force t))) else filter p (Lazy.force t);; 

let rec map f = function
  | Nil -> Nil
  | Cons(h,t) -> Cons(f h, lazy(map f (Lazy.force t)));;

let rec append a b =
  match a with
  | Nil -> b
  | Cons(h,t) -> Cons(h, lazy (append (Lazy.force t) b));; 
   
let (++) = append;;  

let rec length = function
  | Nil -> 0
  | Cons(h,t) -> 1 + length (Lazy.force t);;

let rec sort = function
  | Nil -> Nil
  | Cons(s,t) -> let xs = Lazy.force t in 
     (sort (filter (fun x-> x<s) xs)) ++ Cons(s, lazy (sort (filter (fun x-> x>=s) xs)));; 

let rec nats_from n =  Cons(n, lazy (nats_from (n+1)));;
let rec random k =  Cons(k mod 100, lazy (rnd ((k*4021+6619) mod 10000)));;
let print_list lst = iter (Printf.printf "%d ") lst; print_endline "";;
Re[23]: Мастер-класс по ФП
От: BulatZiganshin  
Дата: 24.01.09 15:18
Оценка:
Здравствуйте, D. Mon, Вы писали:

DM>Сделал решение на ленивых списках. Из каждого компонента строки-параметра получается функция, которая берет ленивый список файлов и возвращает в виде ленивого списка файлов его начало — столько, сколько удовлетворяют критерию помещения в один блок (будь то количество, размер или что-то еще). Комбинация этих функций строится путем простой композиции. Результат композиции — функция, возвращающая столько файлов из начала данного ей списка, сколько удовлетворяют всем критериям.


ах, красавчег. а я до этого не додумался, использовал (take.min.map length). с использованием твоего трюка полное решение на хаскеле выглядит так:
data File = File {size :: Integer, ext :: String}

-- |Разбивает список файлов по комплексному критерию типа "e10f1000b"
chopper crits = recursively (\xs -> xs.$ splitAt (length (headByCrits crits xs) `max` 1))

-- |Преобразует список критериев в функцию, возвращающую начало списка, которое им всем удовлетворяет
headByCrits =  reverse                        -- -> b0001f01e
           >>> recursively (break1 isAlpha)   -- -> b0001,f01,e
           >>> map headByCrit                 -- -> takeBytes 1000, take 10, takeOneExt
           >>> flip (foldr ($))               -- -> takeBytes 1000 . take 10 . takeOneExt

-- |Преобразует (реверсированный) критерий в функцию, возвращающую соответствующее начало списка
headByCrit "e"     = head . groupOn ext
headByCrit ('f':n) = take      (read $ reverse n)
headByCrit ('b':n) = takeBytes (read $ reverse n)

-- |Возвращает префикс списка файлов с суммарной длиной не более bytes байт
takeBytes bytes files  =  files.$ take (files.$ prefixLen size (+) (<=bytes))

-- |Возвращает длину префикса списка, удовлетворяющего заданной комбинации условий
prefixLen mapper combinator tester  =  length . takeWhile tester . scanl1 combinator . map mapper

-- |Аналог break, который безусловно включает первый символ в первый список
break1 p (x:xs) = mapFst (x:) (break p xs)
Люди, я люблю вас! Будьте бдительны!!!
Re[3]: Мастер-класс по ФП
От: dilmah США  
Дата: 26.01.09 12:08
Оценка:
BZ> сравнить фп и императивные решения на "рабочих" языках

вот "рабочий" язык шелл:

> есть список слов. надо записать в файл "a" все слова, начинающиеся на букву 'a', в файл "b" — слова на 'b' и т.д. вплоть до z


cat words | tr -dc a-z\\n | sed 's/^\(.\)\(.*\)$/echo \1\2 >> \1/' | sh -s
Re[4]: Мастер-класс по ФП
От: thesz Россия http://thesz.livejournal.com
Дата: 26.01.09 20:56
Оценка:
>> есть список слов. надо записать в файл "a" все слова, начинающиеся на букву 'a', в файл "b" — слова на 'b' и т.д. вплоть до z
D>cat words | tr -dc a-z\\n | sed 's/^\(.\)\(.*\)$/echo \1\2 >> \1/' | sh -s

http://users.livejournal.com/_winnie/201552.html
http://users.livejournal.com/_winnie/201740.html
Yours truly, Serguey Zefirov (thesz NA mail TOCHKA ru)
Re[2]: Мастер-класс по ФП
От: R.K. Украина  
Дата: 28.01.09 16:49
Оценка:
Здравствуйте, D. Mon, Вы писали:

DM>Предлагаю продолжить на примере такой задачи:


DM>1. Есть большой текстовый файл A (несколько гигов, заведомо не помещается в память), нужно сделать текстовый файл A1, содержащий все строки из А, но отсортированные в алфавитном порядке. Ограничение по памяти — 256 МБ.


Доделал demos/sort_lin до настоящей внешней сортировки. Использовал, кстати, Generators 2
Автор: remark
Дата: 28.05.08
— генераторы очень помогли в процессе.
Свойства получившейся программы: использует строго не более N Мб ОЗУ (по умолч. 128). Сливаются одновременно не более D (= 4) отсортированных файлов. Максимальный размер строки не более ((N << 19) / D). Ключем считается вся строка вместе с терминальным '\n', дубликаты строк отбрасываются.
Тесты:
Файл (исходники .cs) 50М, после сортировки 16М, N = 1: 6.53 sec; сортировка отсортированного 16М файла, N = 1: 4.21.
Те же файлы, N = 16: 3.29 и 1.64 сек.
Файл (.mp3) 2900M, после сортировки 2680М, N = 256: 17 mins; сортировка отсортированного 2680М файла, N = 256: 12 mins.
Файл (строки Фибоначчи случайной длины, программа генерации) 1024М, после сортировки 884M, N = 128: 200 sec.
You aren't expected to absorb this
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.