2014-10-22 2 views
2

Одним из моих модулей в этом году является программирование Haskell. У меня возникли проблемы с выражением сложности таким простым способом, особенно с других языков, таких как C#.Удаление n числа элементов из списка

Часть процесса ознакомления включает в себя реализацию функции drop, которая удаляет из числа число n элементов.

Напишите функцию drop ':: Int -> [a] -> [a], где drop' n xs возвращает xs с удалением первых n элементов.

До сих пор это то, что я придумал.

drop' :: Int -> [a] -> [a] 
drop' x [] = [] 
drop' x (y : ys) = if x == 0 then ys else drop' (x-1) ys 

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

Я знаю, что выше не дает мне результат я ожидал, как он удаляет более 1, чем я ожидал, но интересно это работает:

drop' :: Int -> [a] -> [a] 
drop' x [] = [] 
drop' x (y : ys) = if x == 1 then ys else drop' (x-1) ys 

И я не могу вывести почему! Любая помощь с логикой этого будет очень признательна!

+1

Число от 5 до 0, будет шесть чисел. Итак, в первом случае вы удаляете 6 чисел, тогда как во втором вы удаляете ровно 5 (считая от 5 до 1). Кроме того, есть опечатка в первом, я думаю, 'If x == 0 then ys', возможно? – thefourtheye

+0

Условие гарантирует, что элементы удаляются только в том случае, если x не равен 0, поэтому в первом случае это должно быть: 5 = 1 удалено, 4 = 2 удалено, 3 = 3 удалено, 2 = 4 удалено, 1 = 5 удалено , Но это не так! – Bradley

ответ

2

Вы не нужно следить за тем, сколько элементов у вас есть снято до сих пор, только у вас еще есть, и это значение параметра x. Итак, у вас уже есть эта часть.

Проблема с вашей первой версией функции заключается в том, что когда x равно 0, вы возвращаете ys, но это только хвост списка, так как вы уже удалили первый элемент с помощью сопоставления шаблонов в списке аргументов. Вам нужно будет использовать if x == 0 then (y:ys), чтобы вернуть весь список ввода.

В качестве альтернативы, вы можете использовать функцию tail в рекурсивном случае вместо сопоставления с образцом:

drop' x ys = if x == 0 then ys else drop' (x-1) (tail ys) 

И вы можете поставить нулевой случай на отдельной строке, вместо того, чтобы использовать if, поэтому в целом определение это:

drop' :: Int -> [a] -> [a] 
drop' _ [] = [] 
drop' 0 ys = ys 
drop' x ys = drop' (x-1) (tail ys) 
+0

Это объясняет это очень хорошо, спасибо! По какой-то причине я подумал о возврате хвоста списка, когда x = 0 на самом деле был весь список, когда он не был. – Bradley

+0

Ваш 'drop'' завершится неудачно, если заданный список пуст, а первый параметр больше нуля. Это не поведение реального «падения». – Mark

+0

@Mark, я хотел бы, чтобы эти две строки были прочитаны как замена одной строки из вопроса, а не полное определение функции, поэтому им неявно предшествуют объявление типа и случай с пустым списком , Но я вижу, как это может быть непонятно, поэтому я отредактировал ответ, чтобы дать полное определение всем четырем строкам. – Wyzard

4

В коде здесь:

drop' x (y : ys) = If x == 0 then ys else drop' (x-1) ys 

Это источник вашей ошибки вне по одному: если x является 0, то вы хотите возвратить весь список изменений (y:ys), а не только хвост (ys). Эквивалентно, вы можете проверить 1 вместо 0, а затем вернуться ys.

Было бы более идиоматических использовать соответствия шаблону вместо if, и вы должны использовать переменную _ не-ухода за ценности, которые вы не используете:

drop' :: Int -> [a] -> [a] 
drop' _ [] = []   -- dropping any number of items from [] is still empty 
drop' 0 lst = lst   -- dropping nothing returns the list unchanged 
drop' n (_:xs) = drop' (n-1) xs -- otherwise remove head and recurse on the tail 
+0

Я отредактировал ошибку xs. Это имеет смысл, но для ознакомления с Haskell нам не разрешено использовать функцию drop, предоставленную в Haskell! – Bradley

+0

@ Брэдли - Я не хотел; «падение» без апострофа было опечаткой. Проверьте отредактированный ответ. –

+1

В вашем ответе вы использовали ключевые слова подчеркивания вместо значений, каков эффект этого и почему он не отличается от использования x, например. EDIT вы используете _, потому что мы удаляем его из списка, поэтому нам не нужна его ценность? – Bradley

0

Следующий подход может оказаться поучительным, даже если рекурсия не используется.Рассмотрим это понимание,

drop' :: Int -> [a] -> [a] 
drop' n xs = [ v | (_,v) <- filter (\(x,_) -> x > n) $ zip [1..] xs ] 

Качества, представляющие интерес в этом примере,

  • мы (лениво) список почтовый xs со значением индекса, начиная с 1 до числа элементов в xs;
  • filter включает в себя лямбда-функцию, которая извлекает первый элемент в кортежей zipped и сравнивает значение с количеством элементов, которые нужно удалить;
  • для каждого отфильтрованного кортежа, мы извлекаем второй элемент, значение из исходного списка.

Тогда, например

drop' 4 ['a'..'t'] 
"efghijklmnopqrst" 
Смежные вопросы