2013-12-13 2 views
2

Как внедрить вставку с помощью складчатости в haskell. Я попытался:Реализация вставки в haskell с foldr

insert'' :: Ord a => a -> [a] -> [a] 
insert'' e xs = foldr (\x -> \y -> if x<y then x:y else y:x) [e] xs 

Нет кости. Мне нужно вставить элемент e в список, чтобы он доходил до первого элемента, который больше или равен ему.

Пример:

insert'' 2.5 [1,2,3] => [1.0,2.0,2.5,3.0] 
insert'' 2.5 [3,2,1] => [2.5,3.0,2.0,1.0] 
insert'' 2 [1,2,1] => [1,2,2,1] 

В последнем примере первый 2 вставляется один.

EDIT: Thanks @Lee.

У меня это сейчас:

insert'' :: Ord a => a -> [a] -> [a] 
insert'' e xs = insert2 e (reverse xs) 
insert2 e = reverse . snd . foldr (\i (done, l) -> if (done == False) && (vj e i) then (True, e:i:l) else (done, i:l)) (False, []) 
    where vj e i = e<=i 

Но это не работает:

insert'' 2 [1,3,2,3,3] => [1,3,2,2,3,3] 
insert'' 2 [1,3,3,4] => [1,3,2,3,4] 
insert'' 2 [4,3,2,1] => [4,2,3,2,1] 

РЕШЕНИЕ:

insert'' :: Ord a => a -> [a] -> [a] 
insert'' x xs = foldr pom poc xs False 
    where 
    pom y f je 
     | je || x > y = y : f je 
     | otherwise = x : y : f True 
    poc True = [] 
    poc _ = [x] 

Благодаря @Pedro Rodrigues (Это просто nedded изменить х > = y до x> y.)

(Как отметить это как ответ?)

+4

складки обычно используются для сокращения списков, а не для их расширения. Кроме того, ваше описание и ваш третий пример противоречат друг другу. По вашему описанию он должен быть вставлен перед 3, как в первом примере. – bheklilr

+0

Извините, я его отредактировал. – excrucio

+0

@bheklilr: Каков наилучший способ решить это? Является ли явная рекурсия или любая функция высшего порядка для решения этого? – Sibi

ответ

2

Вот мое взятие на него:

insert :: Ord a => a -> [a] -> [a] 
insert x xs = foldr aux initial xs False 
    where 
    aux y f done 
     | done || x > y = y : f done 
     | otherwise = x : y : f True 
    initial True = [] 
    initial _ = [x] 

Однако ИМХО используя foldr не наилучшим образом подходит для эта проблема, и для меня следующее решение легче понять:

insert :: Int -> [Int] -> [Int] 
insert x [] = [x] 
insert x [email protected](y : ys) 
    | x <= y = x : z 
    | otherwise = y : insert x ys 
+0

Ваше последнее решение явно рекурсивно. Точка использования 'fodlr' должна быть * неявно * рекурсивной, чтобы позволить' foldr' инкапсулировать рекурсию. Само использование 'foldr' указывает на рекурсию; явно рекурсивный код должен быть интерпретирован (человеческим) умом, поэтому «более чистый» находится в глазах смотрящего. :) –

+0

@WillNess Предоставлено «более чистое» решение очень субъективно, поэтому я изменил формулировку, чтобы заявить, что это просто мое мнение. Проблема также может быть решена с использованием неявной рекурсии с использованием 'unfoldr', но что? Я все еще считаю, что явное рекурсивное решение легче понять, что любой из решений 'foldr'. –

+0

, конечно, я никогда не утверждал, что вы * должны делать то или это. :) - BTW 'unfoldr' инкапсулирует * corecursion *. Просто придираюсь. :) –

1

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

insert' l a = snd $ foldl (\(done, l') b -> if done then (True, l'++[b]) else if a<b then (False, l'++[b]) else (True, l'++[a,b])) (False, []) l 
+0

Это с foldl, и я не понимаю. И почему мой if не работает? – excrucio

+0

@excrucio Ваша программа даже не проверит проверку, поэтому слишком рано ее отлаживать. – user3974391

+0

Хорошо, я знаю, но что я делаю неправильно. Мое «если» мне кажется хорошо, но когда я смотрю на вас, вы используете «\\ (done, l ') b», и я не понимаю, почему это так. Возможно, это глупый вопрос, но я новичок в Haskell. – excrucio

2

Вам нужен paramorphism для этого:

para :: (a -> [a] -> b -> b) -> b -> [a] -> b 
foldr :: (a ->  b -> b) -> b -> [a] -> b 

para c n (x : xs) = c x xs (para c n xs) 
foldr c n (x : xs) = c x (foldr c n xs) 
para c n []  = n 
foldr c n []  = n 

с ним,

insert v xs = para (\x xs ys-> if v <= x then (v:x:xs) else (x:ys)) [v] xs 

Мы можем подражать paramorphisms с foldr над init . tails, как можно видеть здесь: Need to partition a list into lists based on breaks in ascending order of elements (Haskell).


Таким образом, решение

import Data.List (tails) 

insert v xs = foldr g [v] (init $ tails xs) 
    where 
    g [email protected](x:_) ys | v <= x = v : xs 
        | otherwise = x : ys 

Другой способ кодирования paramorphism путем создания вложенной функции цепи, как показано на answer by Pedro Rodrigues, чтобы организовать для слева направо информационный поток:

insert v xs = foldr g (const [v]) xs xs 
    where 
    g x f xs | v > x  = x : f (tail xs) 
      | otherwise = v : xs 

-- the visual aid to how this works, for list [a,b,c,d]: 
-- g a (g b (g c (g d (const [v])))) [a,b,c,d] 

В отличие от веры в его ответе, это не копирует остальную структуру списка после точки вставки (что стало возможным благодаря способности параморфизма «съесть торт и иметь его», т. е. передав вторую копию списка входных данных как дополнительный аргумент).

Смежные вопросы