Haskell: Расширитель уравнений 1+ (1+ (1+ (1+ (…)))) = ∞ - PullRequest
13 голосов
/ 10 декабря 2010

Существует ли расширитель уравнений для Haskell?

Что-то вроде foldr.com : 1+(1+(1+(1+(…))))=∞

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

Например, сначала мне было трудно понять foldr против foldl, пока я не увидел, что они расширились.

foldr :: (a -> b -> b) -> b -> [a] -> b
foldr k z xs = go xs
             where
               go []     = z
               go (y:ys) = y `k` go ys

foldl :: (a -> b -> a) -> a -> [b] -> a
foldl f z0 xs0 = lgo z0 xs0
             where
                lgo z []     =  z
                lgo z (x:xs) = lgo (f z x) xs

Из определений я вижу, что foldr расширяется так:

foldr (+) 0 [1..1000000] -->
1 + (foldr (+) 0 [2..1000000]) -->
1 + (2 + (foldr (+) 0 [3..1000000])) -->
1 + (2 + (3 + (foldr (+) 0 [4..1000000]))) -->
1 + (2 + (3 + (4 + (foldr (+) 0 [5..1000000])))) -->

и foldl расширяются так:

foldl (+) 0 [1..1000000] -->
foldl (+) (foldl (+) 0 [1]) [2..1000000]) --> 
foldl (+) (foldl (+) (foldl (+) 0 [1])) [3..1000000]) --> 

или от Haskell Wiki на foldr fold foldl :

let z1 =  0 + 1
in foldl (+) z1 [2..1000000] -->

let z1 =  0 + 1
    z2 = z1 + 2
in foldl (+) z2 [3..1000000] -->

let z1 =  0 + 1
    z2 = z1 + 2
    z3 = z2 + 3
in foldl (+) z3 [4..1000000] -->

let z1 =  0 + 1
    z2 = z1 + 2
    z3 = z2 + 3
    z4 = z3 + 4
in foldl (+) z4 [5..1000000] -->

Однако у меня проблемы с большими уравнениями, понимающими, почему все работает так, как они работают в Хаскеле. Например, первая функция сита использует 1000 фильтров, в то время как вторая функция сита занимает только 24, чтобы найти простое число 1001.

primes = sieve [2..]
   where
    sieve (p:xs) = p : sieve [x | x <- xs, rem x p /= 0] 



primes = 2: 3: sieve (tail primes) [5,7..]
   where 
    sieve (p:ps) xs = h ++ sieve ps [x | x <- t, rem x p /= 0]  
                                    -- or:  filter ((/=0).(`rem`p)) t
                      where (h,~(_:t)) = span (< p*p) xs

Haskell Wiki на простых числах

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

Кроме того, я думаю, что это также может помочь в вопросах, которые направлены на оптимизацию кода на Haskell:

Есть ли инструмент для расширения выражений на Haskell?

Ответы [ 3 ]

4 голосов
/ 10 декабря 2010

Дэвид В. Спасибо за эти ссылки. Repr определенно стоит добавить в мой ящик для инструментов. Я хотел бы добавить несколько дополнительных библиотек, которые я считаю полезными.

HackageDB: трассировка (по состоянию на 12 декабря 2010 г.)

  • Библиотека и программа ghc-events: Библиотека и инструмент для анализа файлов .eventlog из GHC
  • библиотека капота: отладка путем наблюдения на месте
  • Библиотека hpc-strobe: сгенерированные Hpc стробоскопы для работающей программы на Haskell
  • Программа hpc-tracer: Tracer с интерфейсом AJAX

Кажется, я ищу пакет Hook. Я опубликую больше образцов позже сегодня.

Капот

main = runO ex9

ex9 = print $ observe "foldl (+) 0 [1..4]" foldl (+) 0 [1..4]
* * Выходы тысяча двадцать восемь
10

-- foldl (+) 0 [1..4]
  { \ { \ 0 1  -> 1
      , \ 1 2  -> 3
      , \ 3 3  -> 6
      , \ 6 4  -> 10
      } 0 (1 : 2 : 3 : 4 : []) 
       -> 10
  }

Я не знал о библиотеке Hackage (так как я только вхожу в Haskell). Это напоминает мне о CPAN Perl. Спасибо за предоставление этих ссылок. Это отличный ресурс.

3 голосов
/ 10 декабря 2010

Это никоим образом не является полным ответом на ваш вопрос, но я нашел беседу в Haskell-Cafe, в которой есть несколько ответов:

http://www.haskell.org/pipermail/haskell-cafe/2010-June/078763.html

Эта ветка ссылается на этот пакет:

http://hackage.haskell.org/package/repr, что в соответствии со страницей " позволяет отображать перегруженные выражения в их текстовое представление "

Приведенный пример:

*Repr> let rd = 1.5 + 2 + (3 + (-4) * (5 - pi / sqrt 6)) :: Repr Double
*Repr> show rd
"fromRational (3 % 2) + 2 + (3 + negate 4 * (5 - pi / sqrt 6))"
1 голос
/ 24 декабря 2010

Это ответ на незаданный вопрос, считайте его длинным комментарием.

(Пожалуйста, уменьшите значение ниже 0, если вы считаете, что он не подходит. Iзатем удалим его.)


Как только вы станете немного опытнее, вы, возможно, больше не захотите видеть, как все расширяется.Вы захотите понять, КАК все это работает, что заменяет вопрос, ПОЧЕМУ это работает;вы больше не получите много, просто наблюдая за тем, как он расширяется.

Способ анализа кода намного проще, чем вы думаете: просто пометьте каждый параметр / переменную как «оцененный» или «неоцененный»или «подлежит оценке», в зависимости от развития их причинных связей.

Два примера:


1.) fibs

Список всех чисел Фибоначчи:

fibs :: (Num a) => [a]
fibs = 1 : 1 : zipWith (+) fibs (tail fibs)

Первые два элемента уже оценены;Итак, пометьте 3-й элемент (который имеет значение 2) как подлежащий оценке, а все остальные - как неоцененные.Третий элемент будет тогда (+) - комбинацией первых элементов fib и хвостовых, которые будут первым и вторым элементом fib, которые уже помечены как оцененные.Это работает с n-м элементом, подлежащим оценке, и с (n-2) -й и (n-1) -й уже оцененными элементами соответственно.

Вы можете визуализировать это по-разному, т.е.:

  fibs!!(i+0)
+ fibs!!(i+1)
= fibs!!(i+2)

            (fibs)
zipWith(+)  (tail fibs)
        =   (drop 2 fibs)

          1 : 1 : 2 : 3 ...
     (1 :)1 : 2 : 3 : 5 ...
 (1 : 1 :)2 : 3 : 5 : 8 ...

2.) Ваш пример "сито (p: ps) xs"

primes = 2: 3: sieve (tail primes) [5,7..]
   where 
    sieve (p:ps) xs = h ++ sieve ps [x | x <- t, rem x p /= 0]  
                                    -- or:  filter ((/=0).(`rem`p)) t
                      where (h,~(_:t)) = span (< p*p) xs

In "сито (p: ps)xs ",

  • p оценивается,
  • ps не оценивается, а
  • xs является оцененным бесконечным частично-просеянным списком (не содержит p, но содержит p²), который можно угадать, читая рекурсию и / или признавая, что значения h должны быть простыми.

Sieve должна возвращать список простых чисел после p, так что по крайней мере следующее простое число должнобыть оцененным.

  • Следующее простое число будет в списке h, который является списком всех (уже просеянных) чисел k, где p
  • t содержит все числа xs выше p².t следует оценивать как ленивый, а не как можно скорее, потому что может даже не быть необходимости оценивать все элементы в h.(Только первый элемент h должен быть оценен.)

Остальной частью определения функции является рекурсия, где следующим xs является t со всеми n * p, просеянными.


В случае foldr, анализ покажет, что «go» определяется только для ускорения времени выполнения, а не читабельности.Вот альтернативное определение, которое легче проанализировать:

foldr :: (a -> b -> b) -> b -> [a] -> b
foldr (.:) e (x:xs) = x .: (foldr (.:) e xs)
foldr (.:) e []     = e

Я описал его функциональность здесь (без анализа).

Для обучения этого типаанализируя, вы можете прочитать источники некоторых стандартных библиотек;то есть scanl, scanr, unfoldr в источнике из Data.List .

...