【问题标题】:Finding the quickest way with the least changes haskell以最少的变化找到最快的方法 haskell
【发布时间】:2014-12-08 23:37:48
【问题描述】:

一个函数,journey,它获取旅程开始的城市名称和结束城市的名称,并返回进行最少更改次数的旅程。例如,仅考虑曼谷航空公司,

   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")

这将返回旅程,但是,这并不意味着它是最短的。 还有城市的定义它需要更多的修饰,因为我认为它不是有效的。

【问题讨论】:

  • 如果您将城市和航空公司分为两种数据类型:data Place = France | Greece | Singapore | Bangkok | Bago | Yangon | NewDelhi deriving (Eq, Show)data Airline = Lufthansa | BangkokAirways deriving (Eq, Show,编译器肯定会更高效、更轻松地为您提供帮助。然后你可以定义city :: Place -> (Place, Airline, Place),当你错过一个模式匹配时,编译器会警告你(用-Wall)。
  • 你要的是最短路径算法,即Dijkstra算法。有一些图形库可以做到这一点,但如果你想自己编写它,你可能应该从实现图形数据类型开始。

标签: string list haskell tuples


【解决方案1】:

您可以使用称为“打结”的技术。使用这种技术,图被表示为无限树:

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 ys 会生成 z1 : z2 : ... 形式的列表,即使 xsys 是无限的。例如,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)(即,对于某些 xxs,要么是 [] 要么是 x:xs),但是

shortest [1..] [2..]

减少到

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

不在 whnf 中。并且表达式被进一步强制:

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]。所以shortest 函数必须是惰性的,因为path' p2path' p3 在带有循环的图中可以是无限的。

【讨论】:

  • 但是,这将如何与字符串一起工作?因为这只适用于 Char 和 Int
  • shortest 函数非常微妙。这肯定不是“返回最短列表”的最常见实现,因为它会小心地生成 thunk,以便打结过程起作用。
  • @chi,为什么不呢? shortest 很懒惰,但并不陌生。例如这个length $ take 10 $ shortest [1..] [2..] 打印10。如果实施不那么小心,它就不会。
  • 我(表达得很糟糕)的观点是,您的评论“只接收两个列表并返回最短的”并没有说明实现也是惰性的,这对于整个工作方法至关重要。也许这一点应该扩大一点。
  • @chi,感谢您的建议。我已经编辑了答案。
猜你喜欢
  • 2011-01-03
  • 2021-10-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-27
相关资源
最近更新 更多