Eta reduction (Haskell)
От: dr.Chaos Россия Украшения HandMade
Дата: 21.03.07 11:16
Оценка:
Вот решил посмотреть чисто функциональный язык. Выбрал Haskell .

Вот читаю YAHT.

Возникла пара вопросов по сокращению записи функций:

1. Есть пример как выразить ф-ю filter с помощью foldr (7.12)

filter p [] = []
filter p (x:xs) =
if p x
then x : filter p xs
else filter p xs


...
Now, suppose that we call the result of calling filter p xs simply b, then we can rewrite this as:

filter p [] = []
filter p (x:xs) =
if p x then x : b else b

filter p = foldr (\a b -> if p a then a:b else b) []


Вопрос в том, как компилятор(интерпретатор) поймет, что b это filter p xs

2. Чуть ниже приводиться подобный пример для ++

(++) [] ys = ys
(++) (x:xs) ys = x : (xs ++ ys)

Now, the question is whether we can write this in fold notation. First, we can apply eta reduction to the first line to give:

(++) [] = id


Несколько не понял выделенную замену, что она дает и как её понимает компилятор.

Through a sequence of steps, we can also eta-reduce the second line:

(++) (x:xs) ys = x : ((++) xs ys)
==> (++) (x:xs) ys = (x:) ((++) xs ys)
==> (++) (x:xs) ys = ((x:) . (++) xs) ys --Здесь может имелось в виду ((x:) . (++)) xs ys?
==> (++) (x:xs) = (x:) . (++) xs

Побеждающий других — силен,
Побеждающий себя — Могущественен.
Лао Цзы
Re: Eta reduction (Haskell)
От: palm mute  
Дата: 21.03.07 11:38
Оценка: 3 (1)
Здравствуйте, dr.Chaos, Вы писали:

DC>Вот решил посмотреть чисто функциональный язык. Выбрал Haskell .

Нормальный выбор .

DC>
DC>(++) [] ys = ys
DC>(++) (x:xs) ys = x : (xs ++ ys)
DC>

DC>Now, the question is whether we can write this in fold notation. First, we can apply eta reduction to the first line to give:

DC>
DC>(++) [] = id
DC>

DC>Несколько не понял выделенную замену, что она дает и как её понимает компилятор.

Все просто. Эта-конверсия (http://en.wikipedia.org/wiki/Lambda_calculus#.CE.B7-conversion) — это преобразование лямбда-выражений. Если f — функция, то очевидно, что f эквивалентно \x -> f x. Эта-редукция — применение этого правила с избавлением от лишней лямбда-абстракции, т.е. замена \x->f x на f.

Теперь рассмотрим уравнение (++) [] ys = ys.
Его можно переписать как (++) [] = \ys -> ys (правило простое: f x = e всегда можно заменить на f = \x -> e).
\ys -> ys — это, очевидно, функция, возвращающая свой аргумент без изменения, в стандартной библиотеке называется id. Таким образом мы и пришли к
(++) [] = id


DC>

DC>Through a sequence of steps, we can also eta-reduce the second line:

DC>

DC>(++) (x:xs) ys = x : ((++) xs ys)
DC>==> (++) (x:xs) ys = (x:) ((++) xs ys)
DC>==> (++) (x:xs) ys = ((x:) . (++) xs) ys --Здесь может имелось в виду ((x:) . (++)) xs ys?
DC>==> (++) (x:xs) = (x:) . (++) xs
DC>

Здесь тоже все на месте. (++) xs — это пример частичного применения. В результате получается функция, которая получает аргумент list и возвращает xs ++ list.
(x: ) — это функция, которая принимае список list и возвращает список с добавленным элементом (x:list).

Композиция этих функций ( (x: ) . (++) xs ), получая аргумент list, сначала вычисляет xs ++ list, затем добавляет новую голову x.
Re[2]: Eta reduction (Haskell)
От: dr.Chaos Россия Украшения HandMade
Дата: 21.03.07 11:47
Оценка:
Здравствуйте, palm mute, Вы писали:

PM>Здравствуйте, dr.Chaos, Вы писали:



DC>>

DC>>Through a sequence of steps, we can also eta-reduce the second line:

DC>>

DC>>(++) (x:xs) ys = x : ((++) xs ys)
DC>>==> (++) (x:xs) ys = (x:) ((++) xs ys)
DC>>==> (++) (x:xs) ys = ((x:) . (++) xs) ys --Здесь может имелось в виду ((x:) . (++)) xs ys?
DC>>==> (++) (x:xs) = (x:) . (++) xs
DC>>

PM>Здесь тоже все на месте. (++) xs — это пример частичного применения. В результате получается функция, которая получает аргумент list и возвращает xs ++ list.

Хм, тогда почему она записана так странно? Частичное применение ведь записывается как (++xs), насколько я понял, или оно
такие записи эквивалентны: (++xs) ys и ((++) xs) ys ?
Побеждающий других — силен,
Побеждающий себя — Могущественен.
Лао Цзы
Re: Eta reduction (Haskell)
От: deniok Россия  
Дата: 21.03.07 11:54
Оценка:
Здравствуйте, dr.Chaos, Вы писали:

DC>Вот решил посмотреть чисто функциональный язык. Выбрал Haskell .


DC>Вот читаю YAHT.


DC>Возникла пара вопросов по сокращению записи функций:


DC>1. Есть пример как выразить ф-ю filter с помощью foldr (7.12)


DC>

DC>

DC>filter p [] = []
DC>filter p (x:xs) =
DC>if p x
DC>then x : filter p xs
DC>else filter p xs


DC>...
DC>Now, suppose that we call the result of calling filter p xs simply b, then we can rewrite this as:

DC>
DC>filter p [] = []
DC>filter p (x:xs) =
DC>if p x then x : b else b

DC>filter p = foldr (\a b -> if p a then a:b else b) []
DC>


DC>Вопрос в том, как компилятор(интерпретатор) поймет, что b это filter p xs


По-моему, это сказано, чтобы просто пояснить переход от рукописной рекурсии к рекурсии, уже встроенной в fold. Компилятору это понимать не надо. Функция
foldr :: (a -> b -> b) -> b -> [a] -> b

работает так
foldr op r [x, y, z]      это      x `op` (y `op` (z `op` r))

Выражение (foldr op r) имеет тип [a] -> b, то есть ожидает списка, чтобы его свернуть. У нас r — это пустой список, а функция op, реализованная через лямбду,
\a b -> if p a then a:b else b

имеет тип
a -> [a] -> [a]

и при выполнении предиката добавляет свой первый аргумент в голову второго, а при невыполнении — нет. При выполнении каждого `op` в цепочке наша лямбда делает свою работу, формируя отфильтрованный список.
Re[3]: Eta reduction (Haskell)
От: palm mute  
Дата: 21.03.07 11:54
Оценка:
Здравствуйте, dr.Chaos, Вы писали:

DC>Хм, тогда почему она записана так странно? Частичное применение ведь записывается как (++xs), насколько я понял, или оно

DC>такие записи эквивалентны: (++xs) ys и ((++) xs) ys ?

Хаскелл — плохой язык для начала изучения ФП именно потому, что в нем куча сахара. Инфиксный оператор, взятый в скобки, превращается в обычную функцию, т.е.
(+) 2 3  <====> 2 + 3
(++) [1,2] [3,4,5] <===> [1,2]++[3,4,5]


Если один из аргументов инфиксного оператора взять в скобки, можно выбирать, будет он левым или правым:
(2/)  <===> \x -> 2/x
(/2)  <===> \x -> x/2
(x:)  <===> \xs -> x:xs
Re: Eta reduction (Haskell)
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 21.03.07 12:00
Оценка:
Здравствуйте, dr.Chaos, Вы писали:

DC>Вопрос в том, как компилятор(интерпретатор) поймет, что b это filter p xs


Это следует из определения foldr.

DC>Несколько не понял выделенную замену, что она дает и как её понимает компилятор.


Эта не замена, это применение эта-редукции к строке [] ++ ys = ys. Компилятор как понимает не знаю, тут автор не об этом говорит (насколько я понял), а о замене сверткой рекурсивного определения функции над списком. Дает она

DC>==> (++) (x:xs) ys = ((x . (++) xs) ys --Здесь может имелось в виду ((x . (++)) xs ys?


Нет. Т.к. из

(.) :: (b -> c) -> (a -> b) -> (a -> c)
(x:) :: [t] -> [t]


следует, что

((x:) .) :: (a -> [t]) -> (a -> [t])


А т.к. второй параметр у нас

(++) :: [t] -> [t] -> [t],


то подставить его как значение типа (a -> [t]) мы не сможем, т.к. [t] не ложится на [t] -> [t]

Что касается самой темы — перевод определения (++) в свёртку, то тут проще рассуждать так.
Свёртка для нулевого списка (первый аргумент) возвращает второй список. Т.е. мы можем просто считать, что второй аргумент (ys) — это начальный элемент свертки (т.е. тот, который возвращается, если список свертки пустой), а первый (xs) и есть собственно сворачиваемый список:

xs ++ ys = foldr f ys xs


где нам надо найти f. Т.к. foldr пробегается справа налево по xs:

f x0 (f x1 (f x2 ... (f xN ys)))


то логично предположить, что f — это просто конструктор списка (. Тогда

xs ++ ys = foldr (:) ys xs


или

(++) = flip (foldr (:))


Это интуитивный вывод определения, можно сделать вывод более формально, без всех этих "можно заметить", "логично предположить".
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[3]: Eta reduction (Haskell)
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 21.03.07 12:00
Оценка:
Здравствуйте, dr.Chaos, Вы писали:

DC>Хм, тогда почему она записана так странно? Частичное применение ведь записывается как (++xs), насколько я понял, или оно

DC>такие записи эквивалентны: (++xs) ys и ((++) xs) ys ?

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

(^2) `map` [1..10]


У операторов — наоборот: по умолчанию инфиксная. Для того, чтобы оператор сделать префиксным необходимо заключить его в скобки:

(+) 1 2


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

(xs++) == \ys -> xs ++ ys
(+xs) == \ys -> ys ++ xs
(++) xs == \ys -> ((++) xs) ys == \ys -> (++) xs ys == \ys -> xs ++ ys
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re: Eta reduction (Haskell)
От: palm mute  
Дата: 21.03.07 12:02
Оценка: 1 (1) +1
Здравствуйте, dr.Chaos, Вы писали:

DC>Вопрос в том, как компилятор(интерпретатор) поймет, что b это filter p xs

Не заметил вопроса о foldr. fold — это настолько базовая для ФП вещь, что она хорошо разобрана в других источниках. В классической статье Why Functional Programming Matters, например.

В SICP тоже разбирается достаточно подробно.
Re: Eta reduction (Haskell)
От: Кодт Россия  
Дата: 21.03.07 12:34
Оценка: 3 (1)
Здравствуйте, dr.Chaos, Вы писали:

DC>1. Есть пример как выразить ф-ю filter с помощью foldr (7.12)

DC>Вопрос в том, как компилятор(интерпретатор) поймет, что b это filter p xs

Идея здесь вот какая: foldr, грубо говоря, заменяет конструктор конс-пары : на пользовательскую функцию.
Т.е.
-- было
src = [1,2,3,4]
src = 1 : 2 : 3 : 4 : []  -- не забываем о правой ассоциативности
-- стало
dst = foldr foo end src
dst = (1 `foo` (2 `foo` (3 `foo` (4 `foo` end))))

Теперь ещё раз посмотрим на фильтр.
filter p  (x:xs) = if p x then x:filtered_xs else filtered_xs where
    filtered_xs = filter p xs

-- представим себе, что filtered_xs мы уже смогли вычислить.
filter_one p x ys = if p x then x:ys else ys

filter p x:xs = (filter_one p) x ((filter p) xs)

-- то есть, (filter_one p) и есть та самая искомая foo из объявления foldr
filter p xs = foldr (filter_one p) [] xs


DC>2. Чуть ниже приводиться подобный пример для ++

DC>(++) [] = id

DC>Несколько не понял выделенную замену, что она дает и как её понимает компилятор.
Это просто трюк, чтобы не писать лишнего (и заодно упростить жизнь компилятору). Эквивалентно выражению
(++) [] ys = ys

что значит: если первый список пуст, то результат склейки — это второй список.
... << RSDN@Home 1.2.0 alpha rev. 655>>
Перекуём баги на фичи!
Re[2]: Eta reduction (Haskell)
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 21.03.07 12:40
Оценка: 68 (3)
Здравствуйте, palm mute, Вы писали:

PM>Не заметил вопроса о foldr. fold — это настолько базовая для ФП вещь, что она хорошо разобрана в других источниках. В классической статье Why Functional Programming Matters, например.


PM>В SICP тоже разбирается достаточно подробно.


Более сложные примеры (расширение свертки на другие типы) неплохо объясняется у Erik Meijer в Merging Monads and Folds for Functional Programming.

Ну и ещё можно посоветовать его же и соавторов классическую (и более формальную) Functional Programming with Bananas, Lenses, Envelopes and Barbed Wire.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[3]: Eta reduction (Haskell)
От: palm mute  
Дата: 21.03.07 12:44
Оценка: +1
Здравствуйте, lomeo, Вы писали:

L>Более сложные примеры (расширение свертки на другие типы) неплохо объясняется у Erik Meijer в Merging Monads and Folds for Functional Programming.

Спасибо, почитаем.

L>Ну и ещё можно посоветовать его же и соавторов классическую (и более формальную) Functional Programming with Bananas, Lenses, Envelopes and Barbed Wire.


А вот линзы с бананами давать начинающему я бы не советовал. Катаморфизмы с анаморфизмами прекрасны, я не спорю, но есть опасность получить обратный эффект .
Re[4]: Eta reduction (Haskell)
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 21.03.07 12:55
Оценка:
Здравствуйте, palm mute, Вы писали:

PM>А вот линзы с бананами давать начинающему я бы не советовал.


Согласен, это я так — в копилку. Вообще список литературы нужен с кейвордами. citeseer — не то, там рыться
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[5]: Список литературы (was Eta reduction (Haskell))
От: palm mute  
Дата: 21.03.07 13:06
Оценка: +1
Здравствуйте, lomeo, Вы писали:

L>Согласен, это я так — в копилку. Вообще список литературы нужен с кейвордами. citeseer — не то, там рыться


Мне пока del.icio.us хватает. CiteULike, на первый взгляд, еще лучше подходит для этой цели. А вообще интересно, почему citeseer цитаты из статей выдирать умеет, а кейворды — нет, они же практически всегда под абстрактами присутствуют.
Re[6]: Список литературы (was Eta reduction (Haskell))
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 21.03.07 13:26
Оценка:
Здравствуйте, palm mute, Вы писали:

PM>Мне пока del.icio.us хватает. CiteULike, на первый взгляд, еще лучше подходит для этой цели. А вообще интересно, почему citeseer цитаты из статей выдирать умеет, а кейворды — нет, они же практически всегда под абстрактами присутствуют.


Я на CiteULike но редко. А так — да, в принципе хватает.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[4]: Eta reduction (Haskell)
От: dr.Chaos Россия Украшения HandMade
Дата: 21.03.07 14:10
Оценка:
Здравствуйте, palm mute, Вы писали:

PM>Здравствуйте, dr.Chaos, Вы писали:


DC>>Хм, тогда почему она записана так странно? Частичное применение ведь записывается как (++xs), насколько я понял, или оно

DC>>такие записи эквивалентны: (++xs) ys и ((++) xs) ys ?

PM>Хаскелл — плохой язык для начала изучения ФП именно потому, что в нем куча сахара. Инфиксный оператор, взятый в скобки, превращается в обычную функцию, т.е.

PM>
PM>(+) 2 3  <====> 2 + 3
PM>(++) [1,2] [3,4,5] <===> [1,2]++[3,4,5]
PM>


PM>Если один из аргументов инфиксного оператора взять в скобки, можно выбирать, будет он левым или правым:

PM>
PM>(2/)  <===> \x -> 2/x
PM>(/2)  <===> \x -> x/2
PM>(x:)  <===> \xs -> x:xs
PM>


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

Я эти концепции понимаю, у меня как раз сахар вопросы вызывает .
Побеждающий других — силен,
Побеждающий себя — Могущественен.
Лао Цзы
Re[2]: Eta reduction (Haskell)
От: dr.Chaos Россия Украшения HandMade
Дата: 21.03.07 14:30
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, dr.Chaos, Вы писали:


DC>>1. Есть пример как выразить ф-ю filter с помощью foldr (7.12)

DC>>Вопрос в том, как компилятор(интерпретатор) поймет, что b это filter p xs

К>Идея здесь вот какая: foldr, грубо говоря, заменяет конструктор конс-пары : на пользовательскую функцию.


К>Теперь ещё раз посмотрим на фильтр.

К>
К>filter p  (x:xs) = if p x then x:filtered_xs else filtered_xs where
К>    filtered_xs = filter p xs

К>-- представим себе, что filtered_xs мы уже смогли вычислить.
К>filter_one p x ys = if p x then x:ys else ys

К>filter p x:xs = (filter_one p) x ((filter p) xs)

К>-- то есть, (filter_one p) и есть та самая искомая foo из объявления foldr
К>filter p xs = foldr (filter_one p) [] xs
К>


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

filter p [] = []
filter p (x:xs) =
if p x
then x : filter p xs
else filter p xs

filter p [] = []
filter p (x:xs) =
if p x then x : b else b


Эти две функции не эквивалентны
Побеждающий других — силен,
Побеждающий себя — Могущественен.
Лао Цзы
Re[3]: Eta reduction (Haskell)
От: Кодт Россия  
Дата: 21.03.07 15:57
Оценка: 8 (1)
Здравствуйте, dr.Chaos, Вы писали:

DC>Меня просто смутила замена

DC>filter p [] = []
DC>filter p (x:xs) =
DC>if p x
DC>then x : filter p xs
DC>else filter p xs

DC>filter p [] = []
DC>filter p (x:xs) =
DC>if p x then x : b else b

DC>Эти две функции не эквивалентны

Там забыто ... where b = filter p xs
тогда эти функции эквивалентны.
После чего авторы предлагают такой паттерн рефакторинга:
foo [] = []
foo x:xs = bar x b where b = foo xs

-- заменить на
foo = foldr (\x b -> bar x b) []

где foo — возможно, каррированная функция (например, filter p), а bar x b — некое выражение, содержащее x и b, но не содержащее xs
... << RSDN@Home 1.2.0 alpha rev. 655>>
Перекуём баги на фичи!
Re[4]: Eta reduction (Haskell)
От: dr.Chaos Россия Украшения HandMade
Дата: 21.03.07 16:28
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, dr.Chaos, Вы писали:


DC>>Меня просто смутила замена

К>
DC>>filter p [] = []
DC>>filter p (x:xs) =
DC>>if p x
DC>>then x : filter p xs
DC>>else filter p xs

DC>>filter p [] = []
DC>>filter p (x:xs) =
DC>>if p x then x : b else b
К>

DC>>Эти две функции не эквивалентны

К>Там забыто ... where b = filter p xs

К>тогда эти функции эквивалентны.
К>После чего авторы предлагают такой паттерн рефакторинга:
К>
К>foo [] = []
К>foo x:xs = bar x b where b = foo xs

К>-- заменить на
К>foo = foldr (\x b -> bar x b) []
К>

К>где foo — возможно, каррированная функция (например, filter p), а bar x b — некое выражение, содержащее x и b, но не содержащее xs

Да нет этого в YAHT я его недели 2-3 назад скачал .

ЗЫ Хотя так намного понятней
Побеждающий других — силен,
Побеждающий себя — Могущественен.
Лао Цзы
Re[5]: Eta reduction (Haskell)
От: deniok Россия  
Дата: 21.03.07 16:58
Оценка: :)
Здравствуйте, dr.Chaos, Вы писали:

DC>Да нет этого в YAHT я его недели 2-3 назад скачал .


Hal Daume III этим "паттерном рефакторинга" скрытно пользуется, а Кодт его выводит на чистую воду
Re[3]: Eta reduction (Haskell)
От: Трурль  
Дата: 22.03.07 06:16
Оценка: 30 (3)
Здравствуйте, lomeo, Вы писали:

Есть еще специальный A tutorial on the universality and expressiveness of fold.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.