когда Вы разделяете список с помощью x:xs синтаксис, почему он перенесен в круглые скобки? каково значение круглых скобок? почему не [x:xs] или просто x:xs?
Cons-ячейка не должна заключаться в круглые скобки в каждом контексте, но в большинстве случаев это потому, что
Запишите это в свой мозг огненными буквами.
Пример:
length [] = 0
length (x:xs) = 1 + length xs
Если скобки опущены, компилятор подумает, что у вас есть аргумент x
, за которым следует неверно размещенный инфиксный оператор, и будет горько жаловаться. С другой стороны, это нормально
length l = case l of [] -> 0
x:xs -> 1 + length xs
. В этом случае ни x
, ни xs
не могут рассматриваться как часть приложения-функции, поэтому скобки не нужны.
Обратите внимание, что то же замечательное правило , приложение функции связывает более жестко, чем любой инфиксный оператор , это то, что позволяет нам записывать length xs
в 1 + length xs
без каких-либо круглые скобки. Правило инфиксов дает, а правило инфиксов отнимает.
Вы просто используете оператор cons :
, который имеет низкий приоритет. Скобки нужны, чтобы все оставалось правильно.
И вы не используете [x: xs]
, потому что это будет соответствовать списку, единственным элементом которого является список с головой x
и хвостом xs
].
Это связано с синтаксическим анализом.
Помните, двоеточие: - это просто конструктор, написанный с использованием синтаксиса оператора. Так что функция, подобная
foo [] = 0
foo (x:xs) = x + foo xs
, также может быть записана как
foo [] = 0
foo ((:) x xs) = x + foo xs
. Если вы опустите скобку в этой последней строке, ее будет очень трудно разобрать!
:
- это конструктор данных, как и любое другое сопоставление с шаблоном, но с написанным инфиксом. Круглые скобки здесь исключительно из-за приоритета инфиксов; на самом деле они не требуются и могут быть безопасно опущены, если это разрешено правилами приоритета. Например:
> let (_, a:_) = (1, [2, 3, 4]) in a
2
> let a:_ = "xyzzy"
'x'
> case [1, 2, 3] of; a:b -> a; otherwise -> 0;
1
Интересно, что это, похоже, не работает в заголовке лямбды. Не знаю почему.
Как всегда, оператор "сопоставления" связывает более жестко, чем что-либо еще, поэтому чаще всего разделители необходимы, но на самом деле они не часть сопоставления с образцом - иначе вы не t уметь использовать шаблоны вроде (x: y: zs)
вместо (x: (y: zs))
.
Я не знаю точного ответа, но полагаю, что это связано с тем, что можно сопоставить в шаблонах. Сопоставлять можно только конструкторы. Конструкторы могут быть однословными или составными. Посмотрите на следующий код:
data Foo = Bar | Baz Int
f :: Foo -> Int
f Bar = 1
f (Baz x) = x - 1
Конструкторы, состоящие из одного слова, соответствуют как есть. Но составные конструкторы должны быть окружены скобками, чтобы избежать двусмысленности. Если мы пропускаем скобки, это выглядит как сопоставление двух независимых аргументов:
f Baz x = x - 1
Итак, поскольку (:)
составной, он должен быть в скобках. Пропуск скобок для Bar
- это своего рода синтаксический сахар.
ОБНОВЛЕНИЕ: Я понял, что (как отметила Сикора) это следствие приоритета операторов. Это проясняет мои предположения. Приложение функции (которое является просто пробелом между функцией и аргументом) имеет наивысший приоритет. Другие, включая (:), имеют более низкий приоритет. Итак, f x: xs
следует интерпретировать как ((:) (f x)) xs
, что, по-видимому, не то, что нам нужно. В то время как f (x: xs)
интерпретируется как f
, применяемый к x: xs
, который, в свою очередь, (:)
применяется к x
и xs
.