Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is the result of the function not reused?

Tags:

haskell

When I try to code a shortest path algorithm, I run across a strange thing. After floydWarshall function generates adjecency matrix in array form, main function tries to query the array multiple times (in replicateM_ loop).

What I found is that my code is terribly slow. So I put traceShow "doing" at the top of floydWarshall and re-run to find that each res ! (start,end) calls floydWarshall repeatedly.

Why does the array re-generates each time?

Full source with sample input: https://gist.github.com/cwyang/27ab81bee731e6d01bb3a7483fdb748e

floydWarshall :: AdjMatrix (Maybe Int) -> AdjMatrix (Maybe Int)
floydWarshall am = traceShow "doing" $ runST $ do
  arr <- thaw am :: ST s (STArray s (Vertex,Vertex) (Maybe Int))
  sequence_ [ go arr k i j | k <- r, i <- r, j <- r]
  freeze arr
  where ((minb,_), (maxb,_)) = bounds am
        r = [minb..maxb]
        go :: STArray s (Vertex,Vertex) (Maybe Int)
           -> Vertex -> Vertex -> Vertex -> ST s ()
        go arr k i j = do
          ij <- readArray arr (i,j)
          ik <- readArray arr (i,k)
          kj <- readArray arr (k,j)
          case (ik, kj) of
            (Nothing, _) -> return ()
            (_, Nothing) -> return ()
            (Just a, Just b) -> case ij of
              Nothing  -> do
                writeArray arr (i,j) $ Just (a+b)
              (Just c) -> when (c > a+b) $ do
                writeArray arr (i,j) $ Just (a+b)
readInt :: B.ByteString -> Int
readInt = fst . fromJust . B.readInt

main :: IO ()
main = do
  [n,m] <- rl
  edges <- replicateM m $ do
    [from,to,weight] <- rl
    return (from,to,weight)
  [q] <- rl
  let am = buildAdjMatrix (1,n) edges
      res= floydWarshall am
  replicateM_ q $ do
    [start,end] <- rl
    putStrLn . show $ maybe (-1) id (res ! (start,end))
  where rl = map readInt . B.words <$> B.getLine

Sample run:

$ graph < floyd3.txt hs
"doing"     <-- floydWarshall keeps calling
1395
"doing"
975
"doing"
1593
"doing"
1023
"doing"
1521
...
like image 516
Chul-Woong Yang Avatar asked Nov 01 '16 08:11

Chul-Woong Yang


People also ask

Can functions be reused?

Functions are reusable, self-contained pieces of code that are called with a single command. They can be designed to accept arguments as input and return values, but they don't need to do either.

Is function a reusable piece of code?

Another essential concept in coding is functions, which allow you to store a piece of code that does a single task inside a defined block, and then call that code whenever you need it using a single short command — rather than having to type out the same code multiple times.

Can a function only be used once?

It's fine to have a function even if only used once. Blocks of code should ideally only do one thing, perform one computation, one calculation. This makes it easy to work on the flow of your logic, instead of getting bogged down with huge blocks of code.

How do functions help you to reuse code in a program?

How do functions help you to reuse code in a program? Functions help you reuse code in a program because the function can be written once to perform an operation, and then be executed any time it is needed.


1 Answers

Frustratingly, this seems to be caused by the GHC issue "Costly let binding gets duplicated in IO action value".

Using forM_ rather than replicateM_ or using BangPatterns solves this issue.

like image 134
Chul-Woong Yang Avatar answered Oct 13 '22 05:10

Chul-Woong Yang