Здравствуйте, deniok, Вы писали:
D>Активное использование point-free плюс конструкции D>
D>-- для f :: a -> b
D>(f .) :: (c -> a) -> c -> b
D>(. f) :: (b -> c) -> a -> c
D>
Для меня эти конструкции имеют довольно ясный интуитивный смысл:
(.f).g это добавление преобразования f над вторым аргументом перед передачей его в функцию g
(f.).g это добавление преобразования f над результатом функции g с двумя аргументами
Здравствуйте, nikov, Вы писали:
N>(.f).g это добавление преобразования f над вторым аргументом перед передачей его в функцию g N>(f.).g это добавление преобразования f над результатом функции g с двумя аргументами
Кстати, отсюда следует интересный вывод, что (f.) и (.h) коммутируют, то есть (f.).(.h) = (.h).(f.)
Просто я однажды объяснил себе, что такое (f.).g.
Если бы тип a -> a был моноидом, то возведение в степень было бы тривиально: размножить нужное число раз и выполнить mconcat. Но т.к. replicate принимает два аргумента, то это не просто mconcat.replicate, а (mconcat.).replicate. Но на самом деле функцию еще надо завернуть в Endo, а результат развернуть. Отсюда (appEndo.).(mconcat.).(.Endo).replicate
Здравствуйте, nikov, Вы писали:
N>Здравствуйте, nikov, Вы писали:
N>>(.f).g это добавление преобразования f над вторым аргументом перед передачей его в функцию g N>>(f.).g это добавление преобразования f над результатом функции g с двумя аргументами
N>Кстати, отсюда следует интересный вывод, что (f.) и (.h) коммутируют, то есть (f.).(.h) = (.h).(f.)
И оба сводятся к тройной композиции с дыркой посередине \ g -> f . g . h
f (g (h x)) = (f . (g . h)) x = ((f .) ((. h) g)) x = ((f .) . (. h)) g x
f (g (h x)) = ((f . g) . h) x = ((. h) ((f .) g)) x = ((. h) . (f .)) g x
Для меня этот код тоже малочитаемый Поэтому решил задачки по-своему. Покритикуйте...
N>1) Написать функцию clone, которая повторяет каждый символ в строке (или в списке) заданное число раз. Например clone 2 "abc" должно дать "aabbcc".
clone xs = xs >>= replicate 2
N>2) Написать функцию power, которая повторяет строку (или список) заданное число раз. Например power 2 "abc" должно дать "abcabc".
power n xs = concat $ replicate n xs
N>3) Написать функцию fpower :: Int -> (a -> a) -> (a -> a), которая возводит функцию в указанную степень. Например fpower 3 (*2) должно вернуть функцию эквивалентную троекратному применению (*2), то есть (*2).(*2).(*2) или (*8).
fpower n fun = (!! n) . iterate fun
N>4) Написать функцию rotateLeft, которая производит циклическую перестановку элементов списка влево на указанное число позиций. Будет ли твой код работать, если список бесконечный? А если указанное число позиций больше, чем длина списка?
МС>>Да, спасибо, это оно самое. МС>>Попытался расписать типизацию на бумажке — чуть мозг не свернул
L>На самом деле ничего страшного здесь нет. Просто никогда так не пиши
А по-моему всё предельно понятно. Почему бы так не написать? Мне тоже сразу придумался именно этот код.
Здравствуйте, Мишень-сан, Вы писали:
МС>Ну для меня этот код пока что слабочитабелен. Haskell я начал изучать несколько дней назад.
Ну тогда еще тренировочные задачки
1) Написать функцию clone, которая повторяет каждый символ в строке (или в списке) заданное число раз. Например clone 2 "abc" должно дать "aabbcc".
2) Написать функцию power, которая повторяет строку (или список) заданное число раз. Например power 2 "abc" должно дать "abcabc".
3) Написать функцию fpower :: Int -> (a -> a) -> (a -> a), которая возводит функцию в указанную степень. Например fpower 3 (*2) должно вернуть функцию эквивалентную троекратному применению (*2), то есть (*2).(*2).(*2) или (*8).
4) Написать функцию rotateLeft, которая производит циклическую перестановку элементов списка влево на указанное число позиций. Будет ли твой код работать, если список бесконечный? А если указанное число позиций больше, чем длина списка?
Попробуй написать в point-free style c использованием существующих комбинаторов.
Здравствуйте, nikov, Вы писали:
N>Ну тогда еще тренировочные задачки N>3) Написать функцию fpower :: Int -> (a -> a) -> (a -> a), которая возводит функцию в указанную степень. Например fpower 3 (*2) должно вернуть функцию эквивалентную троекратному применению (*2), то есть (*2).(*2).(*2) или (*8).
Спасибо, nikov! Отлично голову поломал
Можно ли короче и более читабельно на F#?
Здравствуйте, nikov, Вы писали:
N>А по-моему всё предельно понятно. Почему бы так не написать? Мне тоже сразу придумался именно этот код.
Для тебя. Мне сложнее, смотри.
(map . (,))
Функция двух аргументов — композиция — функция двух аргументов. Да, понятно, т.к. использовал очень часто. Но не предельно, т.е. не интуитивно.
Поэтому стараюсь писать map (\x -> (i,x)), а так как количество вложенности лямбд превышает один уровень, то именую и выношу в where. Конечная запись кажется гораздо понятнее
go i = map $ (,) i
Всё это, разумеется, IMHO. Вот ещё одно IMHO: (,) мне кажется безобразным в этом случае. Когда появится сечение (i,) будет намного симпатичнее. А пока я пишу так:
Здравствуйте, Пельмешко, Вы писали:
П>Здравствуйте, nikov, Вы писали:
N>>Ну тогда еще тренировочные задачки N>>3) Написать функцию fpower :: Int -> (a -> a) -> (a -> a), которая возводит функцию в указанную степень. Например fpower 3 (*2) должно вернуть функцию эквивалентную троекратному применению (*2), то есть (*2).(*2).(*2) или (*8).
П>Спасибо, nikov! Отлично голову поломал П>Можно ли короче и более читабельно на F#? П>
Здравствуйте, nikov, Вы писали:
N>(.f).g это добавление преобразования f над вторым аргументом перед передачей его в функцию g N>(f.).g это добавление преобразования f над результатом функции g с двумя аргументами
Узнаю азбуку Морзе!
Я бы в этом случае, скорее, воспользовался стрелками.
Здравствуйте, nikov, Вы писали:
N>Для меня эти конструкции имеют довольно ясный интуитивный смысл:
N>(.f).g это добавление преобразования f над вторым аргументом перед передачей его в функцию g N>(f.).g это добавление преобразования f над результатом функции g с двумя аргументами
Вчера голову всю изломал чтобы сформулировать этот смысл, а всё так просто...
Эх, в ocaml страшнее выглядит:
Здравствуйте, nikov, Вы писали:
N>Ну тогда еще тренировочные задачки
Вот еще интересная задачка:
5) Написать функцию vector :: Int -> [a] -> [ [a] ], чтобы vector n xs давало всевозможные списки длиной n, у которых каждый элемент независимо пробегает значения из списка xs (вариант посложнее: функция vector должна работать, даже если xs — бесконечный список).
Здравствуйте, nikov, Вы писали:
N>чтобы vector n xs давало всевозможные списки длиной n, у которых каждый элемент независимо пробегает значения из списка xs
Другими словами, получить все строки длиной n в алфавите xs. Наверное, было бы лучше назвать функцию strings.
Здравствуйте, nikov, Вы писали:
N>5) Написать функцию vector :: Int -> [a] -> [ [a] ], чтобы vector n xs давало всевозможные списки длиной n, у которых каждый элемент независимо пробегает значения из списка xs (вариант посложнее: функция vector должна работать, даже если xs — бесконечный список).
Здравствуйте, Буравчик, Вы писали:
N>>1) Написать функцию clone, которая повторяет каждый символ в строке (или в списке) заданное число раз. Например clone 2 "abc" должно дать "aabbcc". Б>
clone xs = xs >>= replicate 2
Кажется, ты забыл принимать 2 в качестве параметра.
Я бы написал эту функцию
concatMap.replicate
N>>2) Написать функцию power, которая повторяет строку (или список) заданное число раз. Например power 2 "abc" должно дать "abcabc". Б>
power n xs = concat $ replicate n xs
Это тоже самое, что
(concat.).replicate -- передаем 2 аргумента в replicate, а у результата вызываем concat
N>>3) Написать функцию fpower :: Int -> (a -> a) -> (a -> a), которая возводит функцию в указанную степень. Например fpower 3 (*2) должно вернуть функцию эквивалентную троекратному применению (*2), то есть (*2).(*2).(*2) или (*8). Б>
fpower n fun = (!! n) . iterate fun
Очень хорошее решение.
N>>4) Написать функцию rotateLeft, которая производит циклическую перестановку элементов списка влево на указанное число позиций. Будет ли твой код работать, если список бесконечный? А если указанное число позиций больше, чем длина списка? Б>
rotateLeft n xs = drop n xs ++ take n xs
Список не будет периодически прокручиваться при больших n.
Ну и еще одна задачка:
6) Написать функцию tally :: Eq a => [a] -> [(a, Int)], которая подсчитает количество появлений каждого элемента в списке, одновременно удаляя дубликаты.
Здравствуйте, nikov, Вы писали:
N>Здравствуйте, nikov, Вы писали:
N>Ну и еще одна задачка: N>6) Написать функцию tally :: Eq a => [a] -> [(a, Int)], которая подсчитает количество появлений каждого элемента в списке, одновременно удаляя дубликаты.
Здравствуйте, VoidEx, Вы писали:
N>>6) Написать функцию tally :: Eq a => [a] -> [(a, Int)], которая подсчитает количество появлений каждого элемента в списке, одновременно удаляя дубликаты.
VE>
Здравствуйте, nikov, Вы писали:
N>6) Написать функцию tally :: Eq a => [a] -> [(a, Int)], которая подсчитает количество появлений каждого элемента в списке, одновременно удаляя дубликаты.
Я не понимаю, зачем использовать pointfree где попало?
Трудно читаем, трудно исправляем...
Здравствуйте, deniok, Вы писали:
D>Это похоже на рождение нового стиля, nikov-style
Я вот подумал — нельзя ли припахать какой-нибудь изящные рукодельные инфиксные комбинаторы, чтобы вместо лямбды (\x y z -> f x g y h z) навалять (f `_x` g `_y` h `_z`)?
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, deniok, Вы писали:
D>>Это похоже на рождение нового стиля, nikov-style
К>Я вот подумал — нельзя ли припахать какой-нибудь изящные рукодельные инфиксные комбинаторы, чтобы вместо лямбды (\x y z -> f x g y h z) навалять (f `_x` g `_y` h `_z`)?
Боюсь сильно универсально не получится, из-за строгости типизации, но попробовать можно.
Здравствуйте, deniok, Вы писали:
К>>Я вот подумал — нельзя ли припахать какой-нибудь изящные рукодельные инфиксные комбинаторы D>Боюсь сильно универсально не получится, из-за строгости типизации, но попробовать можно.
Нужно только подумать о базисе. Возможно, там будут нумерованные аналоги обоих комбинаторов — $ и . — что-нибудь типа %$, %$$, %$$$, %., %.., %...
Здравствуйте, Кодт, Вы писали:
К>Нужно только подумать о базисе. Возможно, там будут нумерованные аналоги обоих комбинаторов — $ и . — что-нибудь типа %$, %$$, %$$$, %., %.., %...
Здравствуйте, lomeo, Вы писали:
К>>Нужно только подумать о базисе. Возможно, там будут нумерованные аналоги обоих комбинаторов — $ и . — что-нибудь типа %$, %$$, %$$$, %., %.., %... L>Второе рождение перла?
Здравствуйте, nikov, Вы писали:
N>Ну тогда еще тренировочные задачки
Спасибо, неплохо голову поломал. N>1) Написать функцию clone, которая повторяет каждый символ в строке (или в списке) заданное число раз. Например clone 2 "abc" должно дать "aabbcc".
Нужно получить clone :: Int->[Char]->[Char]
Есть replicate :: Int->a->[a]. Допустим, что есть такая f что f.replicate = clone . Тогда f :: (a->[a])->[a]->[a] — так это же concatMap (тут нам поможет hoogle). Получаем ответ:
clone = concatMap . replicate
N>2) Написать функцию power, которая повторяет строку (или список) заданное число раз. Например power 2 "abc" должно дать "abcabc".
Тут хочется скомбинировать уже зарекомендовавшего себя replicate и concat. Опять нужно получить f :: (String->[String])->String->String, или ([Char]->[Char]])->[Char]->[Char]. Это уже можно переписать как ([a]->[a]])->([a]->[a]). То есть нужно сконструировать функцию, которая принимает функцию вида [a]->[a]] и выдает функицю вида [a]->[a] использую concat для катенации полученного массива. Тут можно догадаться что такая функция — это (concat .) В итоге получаем:
power = ((concat.) . replicate)
N>3) Написать функцию fpower :: Int -> (a -> a) -> (a -> a), которая возводит функцию в указанную степень. Например fpower 3 (*2) должно вернуть функцию эквивалентную троекратному применению (*2), то есть (*2).(*2).(*2) или (*8).
Можно заметить, что эта задача похожа на предыдущую. Запишем сигнатуру так: fpower :: Int -> f ->f, причем заметим, что у нас есть операция concatf :: [f] -> f (композиция функций, concatf = foldr1 (.)) В этом случае нужно просто заменить concat из предыдущей задачи на наш concatf, и получим:
fpower = ((foldr1 (.)).) . replicate
PS Последний пример явно показывает, что писать код проще чем его читать. Написать это я смог, а вот прочитать — думаю что не смог бы.
Здравствуйте, nikov, Вы писали:
N>1) Написать функцию clone, которая повторяет каждый символ в строке (или в списке) заданное число раз. Например clone 2 "abc" должно дать "aabbcc".
clone = (id >=>) . replicate
N>2) Написать функцию power, которая повторяет строку (или список) заданное число раз. Например power 2 "abc" должно дать "abcabc".
power = (join.) . replicate
N>3) Написать функцию fpower :: Int -> (a -> a) -> (a -> a), которая возводит функцию в указанную степень. Например fpower 3 (*2) должно вернуть функцию эквивалентную троекратному применению (*2), то есть (*2).(*2).(*2) или (*8).
fpower = (foldl (.) id.) . replicate
N>4) Написать функцию rotateLeft, которая производит циклическую перестановку элементов списка влево на указанное число позиций. Будет ли твой код работать, если список бесконечный? А если указанное число позиций больше, чем длина списка? N>Попробуй написать в point-free style c использованием существующих комбинаторов.
Здравствуйте, nikov, Вы писали:
N>Здравствуйте, nikov, Вы писали:
5) Написать функцию vector :: Int -> [a] -> [ [a] ], чтобы vector n xs давало всевозможные списки длиной n, у которых каждый элемент независимо пробегает значения из списка xs (вариант посложнее: функция vector должна работать, даже если xs — бесконечный список).
vector = (sequence .) . replicate
N>Ну и еще одна задачка: N>6) Написать функцию tally :: Eq a => [a] -> [(a, Int)], которая подсчитает количество появлений каждого элемента в списке, одновременно удаляя дубликаты.