Я определил функцию, которая получает тип данных и список.
getAux :: (Ord a) => SparseArray a -> [Bool] -> (Value a)
getAux (Node x left right) index
| length(index) == 0 = x
| head(index) == False = getAux (left) (tail(index))
| head(index) == True = getAux (right) (tail(index))
Я повторяю эту функцию, передавая хвост из показатель Есть 3 возможных возврата: Если длина списка меньше или равна 1, он возвращает x (значение, хранящееся в узле). Если нет, он проверяет начало индекса и вызывает getAux с хвостом индекса.
Я попытался найти простое решение этой проблемы, добавив дополнительный элемент в конец индекса при вызове getAux. Вместо сравнения, если длина индекса равна 0, я сравниваю его с 1.
Когда я вызываю функцию, у меня есть:
getAux (Nodo x iz de) (num2bin(index) ++ [True])
Новый getAux:
getAux :: (Ord a) => SparseArray a -> [Bool] -> (Value a)
getAux (Node x left right) index
| length(index) == 1 = x
| head(index) == False = getAux (left) (tail(index))
| head(index) == True = getAux (right) (tail(index))
В обоих случаях я получаю сообщение об ошибке, указывающее, что я не могу сделать заголовок пустого списка.
Вы говорите: «Если длина списка меньше или равна 1, он возвращает x», а затем позже «Вместо сравнения, если длина индекса равна 0, я сравниваю его с 1». Разве вы не хотите вместо этого проверить, равна ли длина индекса 0 или 1?
Можете ли вы дать конкретные данные, которые вызывают ошибку, которую вы видите? Я не могу воспроизвести: если я вызываю вашу функцию с [True]
(т. е. в худшем случае, когда num2bin index
возвращает []
), я просто возвращаю x
, который вставил Node x iz de
, как и ожидал.
Вы вызываете это с помощью tail index
, поэтому в конечном итоге вы достигаете пустого списка, следовательно, это объясняет, почему для последнего, если length index == 1
не удается, он попытается получить доступ head index
и, следовательно, ошибка.
Но использование length
не является хорошей идеей, так как оно работает в На) с н длиной списка, и обычно лучше выполнить сопоставление с образцом в списке, чем использовать head
и tail
, поскольку тогда гарантируется, что список имеет head
и tail
.
Таким образом, вы можете реализовать это как:
getAux :: SparseArray a -> [Bool] -> Value a
getAux (Node x _ _) [] = x
getAux (Node _ left right) (x:xs)
| x = getAux right xs
| otherwise = getAux left xs
Включив -Wincomplete-patterns
[Haskell-docs] для компилятора, он предупредит вас о шаблонах, которые функция не охватывает. Например, если у вашего SparseArray
есть дополнительный конструктор данных, то эти случаи не рассматриваются (пока).
Если я не ошибаюсь, если бы у него всегда была голова и хвост, он бы вошел в петлю
@biomaticstudios: нет, пустой список всегда будет ошибкой как в head
, так и в tail
и, таким образом, не является полным. Вот почему некоторые программисты на Haskell считают их «небезопасными» и избегают их использования (а также length
, поскольку это может застрять в бесконечном цикле для бесконечного списка и занимает На) времени).
@biomaticstudios: именно поэтому использование сопоставления с образцом обычно является лучшей идеей: предложение срабатывает только в том случае, если совпадает с образцом, и поэтому мы уверены, что голова x
и хвост xs
существуют, если оно срабатывает для (x:xs)
. Более того, компилятор может выдавать предупреждения о незавершенных шаблонах: шаблоны значений, которые вообще не будут запускать предложение.
Я не уверен, что куплюсь на это объяснение. Предположительно, если length index >= 1
с самого начала и length index == 1
терпит неудачу в каком-то рекурсивном вызове, то length index >= 1
по-прежнему имеет место. Поскольку они призывают вызывать его с num2bin index ++ [True]
в качестве аргумента, для которого длина определенно >= 1, я не понимаю, как будет реализована небезопасная частичность head
(или tail
).
@DanielWagner Я не решаюсь не согласиться с кем-то, чье мнение я так высоко ценю, но лично мне нравится полагаться на компилятор, чтобы проверить, что я рассмотрел все случаи, а не на рассуждения, поэтому я бы предпочел сопоставление шаблонов с -Wincomplete-patterns на чем использовать head
или tail
, если я могу.
@AndrewC Я не думаю, что что-либо из того, что ты сказал, противоречит тому, что я сказал! В частности, я не выносил оценочных суждений о той или иной части кода или о той или иной практике написания кода; только пояснений, что вызвало ошибку.
@DanielWagner Это облегчение!
Можете дать определение
SparseArray
? Вероятно, у него есть другой конструктор данных, кромеNode
?