以最少的变化找到最快的方式haskell

时间:2014-12-08 23:37:49

标签: string list haskell tuples

一个功能,旅程,它以行程开始的城市名称和城市名称结束,并返回一个变化最少的旅程。例如,仅考虑曼谷航空公司,

   journey "Singapore" "Singapore"
   and returns[ ]
  journey "Singapore" "Bangkok"
   and returns [ ("Singapore", "Bangkok Airways", "Bangkok") ]
  journey "Singapore" "New Delhi"
   and returns [ ("Singapore", "Bangkok Airways", "New Delhi") ]

在更大的网络中,

  journey "Singapore" "France"
    ====> [ ("Singapore", "Bangkok Airways", "Greece") ,("Greece", "Lufthansa", "France")]

这是我到目前为止所拥有的

city :: String -> (String,String,String)
city  "Singapore" =("Singapore","Bangkok Airways", "Bangkok")
city  "Bangkok" =("Bangkok","Bangkok Airways", "Bago")
city  "Bago" = ("Bago", "Bangkok Airways", "Yangon")
city  "Yangon" =("Yangon", "Bangkok Airways", "New Delhi")
city  "New Delhi" = ("New Delhi", "Bangkok Airways", "Kiev")
然而,这将返回旅程,并不意味着它是最短的。 还有城市的定义,它需要更多的抛光,因为我不认为它是有效的。

1 个答案:

答案 0 :(得分:4)

您可以使用名为"结扎"的技巧。使用此技术,图形表示为无限树:

data Rose  a = Rose a [Rose a]
data Graph a = Graph [(a, Rose a)]

主要功能很简单:

lookupRose :: Eq a => a -> Graph a -> Rose a
lookupRose i (Graph rs) = fromJust $ lookup i rs

path :: Eq a => a -> a -> Graph a -> [a]
path orig dest gr = path' (lookupRose orig gr) where
    path' (Rose p ps)
        | p == dest = [p]
        | otherwise = p : foldr1 shortest (map path' ps)

我假设,图中没有没有邻居的节点。所以有两种情况:

  • 如果您已经在目的地,请将目的地放在列表中。
  • 否则,搜索到目的地的最短路径并将当前节点添加到其中。

请注意,没有循环检测,但添加它很容易。

shortest函数与图形无关,它只接收两个列表并返回最短的:

shortest :: [a] -> [a] -> [a]
shortest xs ys = snd $ shortest' xs ys where
    shortest'    []     ys  = (True,  [])
    shortest'    xs     []  = (False, [])
    shortest' (x:xs) (y:ys) = case shortest' xs ys of
        ~(b, zs) -> (b, (if b then x else y):zs)

我们需要一个从列表中构建图形的函数:

fromList :: Eq a => [(a, [a])] -> Graph a
fromList xs = graph where
    graph         = Graph $ map irose xs
    irose (i, is) = (i, Rose i $ map (\i -> lookupRose i graph) is)

这就是全部。例如:http://ideone.com/9le557

修改

shortest函数的实现是懒惰的,因此即使shortest xs ysz1 : z2 : ...无限,xs也会生成ys形式的列表。因此,length $ take 10 $ shortest [1..] [2..]会返回10,例如。

我们假设shortest的定义如下:

shortest :: [a] -> [a] -> [a]
shortest xs ys = either id id $ shortest' xs ys where
    shortest'    []     ys  = Left  []
    shortest'    xs     []  = Right []
    shortest' (x:xs) (y:ys) = either (Left . (x:)) (Right . (y:)) $ shortest' xs ys

然后这个表达

take 5 $ shortest [1..10] [2..]

缩减为[1,2,3,4,5]。但

take 5 $ shortest [1..10] (shortest [1..] [2..])

导致堆栈溢出。这是因为shortest要求两个列表都处于弱头正常形式(whnf)(即某些[]x:xs的{​​{1}}或x,但

xs

缩减为

shortest [1..] [2..]

不在whnf中。表达式被迫进一步推动:

either (Left . (1:)) (Right . (2:)) $ ...

等等到堆栈溢出。

但是

either (Left . (1:)) (Right . (2:)) $ either (Left . (2:)) (Right . (3:)) $ ...

缩减为

foldr1 shortest (map path' ps)

如果shortest (path' p1) (shortest (path' p2) (path' p3)) 。因此ps = [p1, p2, p3]函数必须是惰性的,因为shortestpath' p2在具有周期的图形中可以是无限的。