Разбор Haskell: x ++ y: z - PullRequest
       12

Разбор Haskell: x ++ y: z

3 голосов
/ 30 сентября 2019

В Haskell, почему x ++ y : z анализируется как x ++ (y : z), а не как (x ++ y) : z?

Например, [1] ++ 2 : [3] оценивается как [1,2,3].

Оба (++) и (:) являются ассоциативными справа с приоритетом 5.

1 Ответ

11 голосов
/ 30 сентября 2019

Тот факт, что они ассоциативно справа , означает, что они анализируются "справа налево", так сказать. Таким образом, это означает, что x ⊕ y ⊕ z анализируется как x⊕ (y ⊕ z) . Таким образом, это означает, что x ++ y : z действительно анализируется как x ++ (y : z).

Существуют веские причины для правильной ассоциации (:) и (++). Для оператора "cons" (:) это означает, что мы можем написать 1 : 4 : 2 : [], поскольку он анализируется как 1 : (4 : (2: [])), что правильно с точки зрения типов. Если бы он анализировался как ((1:4):2:[]), то, например, 1:4 был бы неправильным, поскольку он ожидает элемент в качестве первого операнда и список этих элементов в качестве второго операнда. Конечно, мы все еще можем позволить анализатору Haskell анализировать его как список, но это приведет к большому количеству лишних скобок.

Для (++) лучше также проанализировать его справа налевоиз-за соображений производительности. x ++ y занимает линейное время в размере x. Так что это означает, что если мы проанализируем x ++ (y ++ z), потребуется | x ​​|+ | y ​​| шагов. Если бы мы проанализировали это как (x ++ y) ++ z, это заняло бы 2 × | x | + | y ​​| , так как в первый раз, когда мы применяем (x ++ y), он работает в размере x, но затем(x ++ y) ++ z работает в размере x ++ y. Таким образом, это будет означать, что если мы объединяем n списков, каждый из которых имеет размер m , он не будет работать в O (n × m) , а в ** тысяча сорок-одна О (п * 1 042 * 2 * 1 043 * × м) . * * тысяча сорок-пять

...