2013-11-22 4 views
2

Я пытаюсь написать функцию, как это:замена элемента в список списков в Haskell

updateMatrix:: [[a]] -> a -> (x, y) ->[[a]] 

Это, как предполагается взять в список списков, таких как:

[ [1, 2, 3, 4], 
    [5, 6, 7, 8]] 

и поместить данный элемент по указанным координатам, поэтому, учитывая:

[ [1, 2, 3, 4], 
    [5, 6, 7, 8]] 9 (0, 1) 

он должен вернуть

[ [1, 9, 3, 4], 
    [5, 6, 7, 8]] 

Я не могу понять, как это сделать, не перестраивая всю матрицу, пожалуйста, помогите!

+4

Это невозможно сделать без «перестроения» матрицы, поскольку все в Haskell неизменно. Тем не менее, «перестроение» матрицы не будет дорогостоящим, поскольку компилятор позаботится об этом, он не будет буквально создавать еще одну совершенно новую матрицу. – rafalio

+0

Вы не можете сделать это, не перестраивая матрицу, поскольку списки неизменны в Haskell. – rightfold

ответ

3

Вот реализация:

updateMatrix :: [[a]] -> a -> (Int, Int) -> [[a]] 
updateMatrix m x (r,c) = 
    take r m ++ 
    [take c (m !! r) ++ [x] ++ drop (c + 1) (m !! r)] ++ 
    drop (r + 1) m 

Но, возможно, это «перестраивается вся матрица», как вы говорите? Обратите внимание, что списки не изменяются в Haskell, поэтому вы не можете разрушить обновление одной записи, если это то, что вы имели бы в виду, не «перестраивая всю матрицу ».

+0

Это сбой при 'r> length m' и приведет к неправильным результатам, если какая-либо координата выходит за пределы. – Evi1M4chine

+0

@ Evi1M4chine: мусор в мусоре: P Я согласен с тем, что в производственном коде мы можем захотеть выполнить проверку ввода, но легко добавить обертку, которая делает это; Мне кажется странным, что вы проголосовали за это. Приятно, что решение Vektorweg (и ваши незначительные вариации) - это идентификация по координатам вне границ, но в некоторых сценариях, которые мы, вероятно, скоро потерпим неудачу, и поэтому мы все ошибаемся. – ntc2

1

Будучи чисто функциональным языком, Haskell требует, чтобы вы возвращали «совершенно новую» матрицу при обновлении элемента, поэтому вам нужно полностью перестроить всю матрицу (если вы действительно заинтересованы в обработке матриц, посмотрите на matrix library, а не на внедрение собственных).

Опасайтесь, списки не подходят для таких манипуляций, но если вы делаете это для образовательных целей, начните с реализации функции, которая «заменяет» элемент в [a], а затем используйте его дважды (состав функций может помочь там) чтобы получить функцию updateMatrix. Here - ответ, который может помочь вам на вашем пути.

5

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

replace :: (a -> a) -> Int -> [a] -> [a] 
replace f 0 (x:xs) = (f x):xs 
replace f i (x:xs) = x : replace f (i-1) xs 
replace f i [] = [] 

replace2D :: (a -> a) -> (Int, Int) -> [[a]] -> [[a]] 
replace2D f (x,y) = replace (replace f y) x 

Ваша функция будет:

updateMatrix ll x c = replace2D (const x) c ll 
+0

Это было бы более элегантно, с складным фоном. – Evi1M4chine

+0

@ Evi1M4chine как? – Vektorweg

+0

Каждый 'f ... = ... x: f xs' является сгибом. И счетчик может перейти к значению init. Но я нашел еще лучшее решение в то же время и разместил его в качестве ответа ниже. – Evi1M4chine

2

Вот короткий:

replace p f xs = [ if i == p then f x else x | (x, i) <- zip xs [0..] ] 
replace2D v (x,y) = replace y (replace x (const v)) 

Теперь вы можете использовать его точно так, как вы хотели:

λ → let m = [[1, 2, 3, 4], [5, 6, 7, 8]] 
λ → replace2D 9 (0, 1) m 
[[1,2,3,4],[9,6,7,8]] 

Как другие уже говорили,

  1. Такой подход, конечно, довольно медленно, и имеет смысл только в случае, если структура является более сложным, чем списки длинны. В Haskell есть легкая документация о внутренней структуре и сложности вещей.
    Подумайте о m как указатель на связанный список указателей, и вы можете понять, почему он медленнее чистого потока байтов. Есть лучшие библиотеки, которые используют что-то более близкое к последнему.
  2. Значения Haskell равны неизменяемым, потому что побочных эффектов нет.Это хорошо для надежности. Таким образом, вы не можете изменить m. Вы можете только что-то построить из m.
  3. Haskell can имитировать mutable ссылки, с помощью монадов. Как IORef. Но использование этого для этого было бы довольно неправильным. Здесь есть много других вопросов о переполнении стека, объясняя его использование, плюсы и минусы.
+0

Подробнее код гольф, меньше понятность. ;) – Vektorweg

+0

Verktorweg: Я предположил, что человек, который кодов Haskell не имеет понимания PHP-скрипача. Это очень простой код для кодера Haskell. Кто играет с комбинаторами трансформаторов монады во сне. :) – Evi1M4chine

+0

его 4 года спустя, и я по-прежнему предпочитаю список монадов по спискам. так что да, я должен смотреть по крайней мере дважды, когда его список comp, так как я не привык к нему и никогда не буду. Заметьте, что я действительно только начинал с haskell в то время, и теперь я буду делать что-то по-другому. Я думаю, что эти два следующих решения подходят лучше, чем ваши и мои. 'replace fi = zipWith (\ jx -> if i == j then fx else x) [0 ..]' или, возможно, даже лучше «replace» fi xs = ys ++ [fx] ++ zs где (ys, x : zs) = splitAt i xs'. с другой стороны, что является полезным решением для тех, кто пытается учиться? – Vektorweg

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