How to make my function move through two lists at once?

允我心安 提交于 2020-01-25 06:51:29

问题


I'm trying to make a function that will take a list of lists of Ints as an input, and adds +1 every time it runs into a number bigger or equal to 10. I added -20 on each side so xc can start at 0. Example what should happen after the function runs into first '10':

     [[-20,-20, 0, 0, 0, 0, 0, 0, 0,-20,-20],
      [-20,-20, 0,10, 1, 0, 0, 0, 0,-20,-20],
      [-20,-20, 1, 1, 1, 0,10, 0, 0,-20,-20],
      [-20,-20, 0, 0, 0,10, 0, 0, 0,-20,-20],
      [-20,-20, 0, 0, 0, 0, 0, 0,10,-20,-20],
      [-20,-20,10,10,10, 0, 0, 0, 0,-20,-20],
      [-20,-20,10, 0,10, 0, 0, 0, 0,-20,-20],
      [-20,-20,10,10,10, 0, 0, 0, 0,-20,-20]]

SampleInput = [[-20,-20, 0, 0, 0, 0, 0, 0, 0,-20,-20],
               [-20,-20, 0,10, 0, 0, 0, 0, 0,-20,-20],
               [-20,-20, 0, 0, 0, 0,10, 0, 0,-20,-20],
               [-20,-20, 0, 0, 0,10, 0, 0, 0,-20,-20],
               [-20,-20, 0, 0, 0, 0, 0, 0,10,-20,-20],
               [-20,-20,10,10,10, 0, 0, 0, 0,-20,-20],
               [-20,-20,10, 0,10, 0, 0, 0, 0,-20,-20],
               [-20,-20,10,10,10, 0, 0, 0, 0,-20,-20]]

adder::[[Int]] -> [[Int]]
adder ((xa:xb:xc:xd:xe):(ya:yb:yc:yd:ye)) 
    | xc >= 10  = adder ((xb:xc:(xd+1):xe):((yb+1):(yc+1):(yd+1):ye))
    | otherwise = adder ((xb:xc:xd:xe):(yb:yc:yd:ye)) 

I also dont know how to apply xa : adder... which we could do fairly easily if it was a single list. Any ideas how to fix this code? Also, you can replace -20 with anything up to 10 if needed, its just for orientation, since I plan to delete these -20s after the function is applied to the list.

I will want to run function second time on a reversed list, you can see why if you imagine minesweeper where 10s are mines


回答1:


I see how you arrived at this idea. In a sense, you are creating a gigantic pattern and move it along the minefield. It can work. But it is not an easy way.

adder :: [[Int]] -> [[Int]]
adder [ ] = [ ]
adder [x] = [x]
adder t@[  [_, _],   [_, _]    ] = t
adder   (u@[_, _]: v@[_, _]: us) = u: v: adder us
adder t = afterPass !! 0: adder (afterPass !! 1: us)
  where
    t' = applyHalfStencil t

    (  (x: xs)
     : (y: ys)
     : us
     ) = t'

    xys = [xs, ys]

    afterPass = [x: adder xys !! 0, y: adder xys !! 1]

applyHalfStencil
      s@( (xa: xb: xc: xs)
        : (ya: yb: yc: ys)
        : us
        )
    | xb > 9 =
        ( (xa    : xb    : xc + 1: xs)
        : (ya + 1: yb + 1: yc + 1: ys)
        : us
        )
    | otherwise = s

Of course, you have to also pad your field with -20s above and below. By the way, one layer of -20 is enough. Sample:

sampleInput :: [[Int]]
sampleInput = [ [-20 , -20 , -20 , -20 , -20 , -20]
              , [-20 , 10  , 0   , 0   , 0   , -20]
              , [-20 , 0   , 10  , 10  , 10  , -20]
              , [-20 , 0   , 10  , 0   , 10  , -20]
              , [-20 , 0   , 10  , 10  , 10  , -20]
              , [-20 , -20 , -20 , -20 , -20 , -20] ]

While we are at it, nothing stops us from applying the whole stencil at once, not just the lower right half of it.

adder :: [[Int]] -> [[Int]]
adder [ ] = [ ]
adder [x] = [x]
adder [x, y] = [x, y]
adder t@[  [_, _], _, _    ] = t
adder   (u@[_, _]: v: w: us) = u: v: w: adder us
adder t = afterPass !! 0: adder (afterPass !! 1: afterPass !! 2: us)
  where
    t' = applyStencil t

    (  (x: xs)
     : (y: ys)
     : (z: zs)
     : us
     ) = t'

    xyzs = [xs, ys, zs]

    afterPass = [x: adder xyzs !! 0, y: adder xyzs !! 1, z: adder xyzs !! 2]

applyStencil
      s@( (xa: xb: xc: xs)
        : (ya: yb: yc: ys)
        : (za: zb: zc: zs)
        : us
        )
    | yb > 9 =
        ( (xa + 1: xb + 1: xc + 1: xs)
        : (ya + 1: yb    : yc + 1: ys)
        : (za + 1: zb + 1: zc + 1: zs)
        : us
        )
    | otherwise = s

So, here we are, another solution. It is probably more efficient. It is also good that you put creative force into the problem.


To the future reader, for context: this question and answer continue the line of inquiry started in our previous cooperation with Jakub.




回答2:


I think it's rather easy, as long as the data structure stays as [[Int]]. See the below example:

addIf10 :: Int -> Int
addIf10 x = if x >= 10 then x + 1 else x

-- The first map "unpacks" the list to form an [Int]
-- Second map runs the addIf10 function on the [Int]
adder :: [[Int]] -> [[Int]]
adder = map (map addIf10)


来源:https://stackoverflow.com/questions/58827023/how-to-make-my-function-move-through-two-lists-at-once

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!