0

I have a function onemove that I am recursively calling upon a list moves. The function onemove returns a 3-tuple (Int,[Char],[[Char]]), namely time, captures, and board . I would like to call the function onemove on the input tuple for each move in the moves list. Below I have provided my code thus far:

    makemoves :: (Int,[Char],[[Char]],[(Int,Int)]) -> (Int,[Char],[[Char]])

    makemoves (time, captures, board, [] ) = (time, captures, board)
    makemoves (time, captures, board, (moveFrom, moveTo):moves ) 
        | checkerAlive board = makemoves (onemove (time, captures, board, move:moves)) 
        | otherwise          = resetGame (time, captures, board)
        where    copyTime = time
                 copyCaptures = captures
                 copyBoard = board
                 move = (moveFrom,moveTo)
                 tempTuple = onemoving (time, captures, board, move)

How might I call the function makemoves recursively? onemovereturns a 3-tuple, while makemoves is expecting a 4-tuple, which is the returned 3-tuple plus the next move. Thanks!

2
  • If this typechecks, why is the type signature of onemove the same as makemoves? Commented Aug 11, 2014 at 19:05
  • I don't get a few things from your code. For example why do you have a copyBoard = board? Also instead of defining move in the where clause you can use @ patterns: move@(moveFrom, moveTo):moves. Regarding your problem, there are some circumstances where it's easier to write a helper function that returns redundant information and write the actual function by calling it and dropping the not-needed information. Lazyness will also avoid to actually compute information that isn't really required. Commented Aug 11, 2014 at 19:08

3 Answers 3

3

onemovereturns a 3-tuple, while makemoves is expecting a 4-tuple, which is the returned 3-tuple plus the next move. Thanks!

No, it's expecting a list of the remaining moves, which you already have in moves.

Remember the other day, when I told you to have your functions take multiple arguments instead of all its arguments in a single tuple? This is exactly why. You should do that now:

makemoves :: [(Int,Int)] -> (Int,[Char],[[Char]]) -> (Int,[Char],[[Char]])

makemoves [] (time, captures, board) = (time, captures, board)
makemoves (move:moves) (time, captures, board)
    | checkerAlive board = makemoves moves (onemove (time, captures, board, move:moves)) 
    | otherwise          = resetGame (time, captures, board)

I'm not sure what all those where clauses are for; none of them are used.

EDIT: Ignore the below, actually, since you need early-exit logic based on the checkerAlive function. It can still be written as a fold, but is probably more work than it's worth at this point.

Also, what you've written here is a very common recursion pattern in functional programming called a "fold". This whole thing can be rewritten as:

makemoves :: [(Int,Int)] -> (Int,[Char],[[Char]]) -> (Int,[Char],[[Char]])
makemoves moves env = foldr onemoveOrReset env moves
onemoveOrReset :: (Int,Int) -> (Int,[Char],[[Char]]) -> (Int,[Char],[[Char]])
Sign up to request clarification or add additional context in comments.

4 Comments

Unfortuantely, my professor sets the requirements on the input/output tuple that is declared so I must work around the idea in some way
@Dev you might want to look at Data.Tuple.Curry
@Dev: that requirement is rather obnoxious. Sometimes it is reasonable to have tuples instead of curried functions, but Haskell programmers generally very much prefer the former. If your professor knows anything about proper Haskell programming, they'll happily accept a curried solution as well.
@Dev I agree with leftaroundabout, as a Haskeller I would much prefer those tuples split up, given names, possibly even full blown record types as it makes the code far more readable, understandable, and clean. Using giant tuples is usually a code smell in Haskell. There are exceptions to this rule, but in general a 2-tuple is about as high as you want to go. I would say that the primary reason for this is because the standard Haskell libraries do not have many tools for working with larger tuples, and that after that point it really helps to give fields explicit names.
1

Lines like

          copyTime = time

are completely superfluent in Haskell: it's never meaningful to copy anything. There's just values, no "objects" which might change at some point, and a value can be used from anywhere. So your entire where block is useless.

To get to your problem, let's consider a simpler example: not moves, but simple numbers you're trying to add. Written with tuples (though, as said by Mark Whitfield, curried functions are better!)

addOne :: (Int,Int) -> Int
addOne (num,inc) = num + inc

makeAdds :: (Int, [Int]) -> Int
makeAdds (num, []) = num

Ok, now the recursive case. At the moment, in your code you have basically

makeAdds (num, inc:incs) = makeAdds (addOne (num, inc:incs))

This is wrong for two reasons:

  • addOne can't use a list, yet you're handing it inc:incs – the very list you've just got from input. Obviously, you want to use just the head element, i.e. inc alone.
  • makeAdds needs two arguments, the second being a list. Well, obviously that's the remaining list!

    makeAdds (num, inc:incs) = makeAdds (addOne (num, inc), incs)

easy enough.

That said, the "correct" version of all this would of course be

addOne :: Int -> Int -> Int
addOne = (+)                   -- Yeah, you can do that.

makeAdds :: [Int] -> Int -> Int  -- Observe I've switched the order:
                                 -- "state" arguments best come last, useful with partial application

makeAdds [] = id
makeAdds (inc:incs) = makeAdds incs . addOne inc

Comments

0

So I figured it out, here is the solution

     makemoves (time, captures, board, (moveFrom, moveTo):moves ) 
        | checkerAlive board = makemoves (newTime,newCaptures,newBoard,moves) 
        | otherwise          = resetGame (time, captures, board)
       where move = (moveFrom,moveTo)
             (newTime,newCaptures,newBoard) = onemoving (time, captures, board, move)

You must first create a temp holder for the first move, thus (newTime,newCaptures,newBoard) then pass that temp holder onto the next move in the moves list with the call to makemoves

1 Comment

Note that you can omit the (moveFrom, moveTo) tuple and just match move:moves immediately, since you don't use either moveFrom or moveTo on its own. Apart from that, this is pretty much as good as it gets with those ugly un-curried functions.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.