2016-04-11 4 views
6

Я пытаюсь создать функцию, которая выбрасывает каждый n-й элемент из строки.Haskell - функция возвращает пустой символ

dropEvery :: String -> Int -> String 
dropEvery str n = map (\(char, indx) -> if indx `mod` n /= 0 then char else ' ') (zip str [1..]) 

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

ответ

7

Вы не можете сделать это только с map , по определению он не может изменить длину используемой коллекции. Однако вы можете заставить это работать без слишком большого количества изменений, переключившись на concatMap. Эта функция требует, чтобы ваша функция отображала список, а затем объединяет все результаты. Все вам нужно сделать, это

dropEvery str n = 
    concatMap (\(char, indx) -> if indx `mod` n /= 0 then [char] else []) (zip str [1..]) 
+0

Я попытался поставить «[]» после «else», но, конечно же, получил ошибки типа. concatMap - хорошая идея. –

+0

@ RaulŠpilev Проблема исходит из предположения, что есть что-то, называемое «пустым« Char ». Это было бы как «пустой' Int' »или« пустой Bool », вы можете иметь' Int', который равен 0, но не тот, у которого нет значения вообще (не считая 'undefined') , Чтобы иметь пустоту, вам нужен контейнер. Строки являются контейнерами, поскольку они просто представляют собой список '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' ], чтобы выйти из '[Char]'. – bheklilr

5

map сохраняет структуру списка, а ваши операции изменяют его, удаляя элементы. Это означает, что вы не можете использовать map, но вы можете использовать mapMaybe, которая позволяет обеспечить функцию, которая возвращает Nothing для элементов, которые вы хотите удалить из выхода:

import Data.Maybe (mapMaybe) 
dropEvery str n = mapMaybe (\(char, indx) -> if indx `mod` n /= 0 then Just(char) else Nothing) (zip str [1..]) 
+0

я должен выполнить некоторые тесты, чтобы убедиться, но это могло бы быть более эффективным решением, чем моя – bheklilr

+0

Это в значительной степени именно то, что я искал. Я думал об использовании «Nothing» и «Just (char)», но не знал, как без ошибок «Не удалось совместить ожидаемый тип». «MapMaybe» решает это. Кроме того, теперь я понимаю, как функция карты работает намного лучше. Спасибо. –

3

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

dropEvery :: Int -> [a] -> [a] 
dropEvery n xs = map fst . filter ((/= n) . snd) $ zip xs (cycle [1..n]) 

Если скорость имеет решающее значение, то, вероятно, будет наиболее эффективно использовать эту технику с явной рекурсией или foldr. Что-то вроде этого:

dropEvery n xs = foldr go (`seq` []) xs n where 
    go _ r 1 = r n 
    go x r k = x : r (k - 1) 
Смежные вопросы