String 用最少的变化找到最快的方法haskell
一个函数,旅程,它取旅程开始的城市名称和结束的城市名称,并返回一个变化最少的旅程。例如,仅考虑曼谷航空公司String 用最少的变化找到最快的方法haskell,string,list,haskell,tuples,String,List,Haskell,Tuples,一个函数,旅程,它取旅程开始的城市名称和结束的城市名称,并返回一个变化最少的旅程。例如,仅考虑曼谷航空公司 journey "Singapore" "Singapore" and returns[ ] journey "Singapore" "Bangkok" and returns [ ("Singapore", "Bangkok Airways", "Bangkok") ] journey "Singapore" "New Delhi" and returns
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 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)
就这些。例如:
编辑
shortest
函数的实现是惰性的,因此shortest xs ys
以z1: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
,要么[]
或x:xs
),但是
减少到
either (Left . (1:)) (Right . (2:)) $ ...
shortest (path' p1) (shortest (path' p2) (path' p3))
这不在whnf中。这句话更进一步:
either (Left . (1:)) (Right . (2:)) $ either (Left . (2:)) (Right . (3:)) $ ...
等等,直到堆栈溢出
但是
减少到
either (Left . (1:)) (Right . (2:)) $ ...
shortest (path' p1) (shortest (path' p2) (path' p3))
如果
ps=[p1、p2、p3]
。因此最短
函数必须是惰性的,因为路径'p2
和路径'p3
在一个具有循环的图中可以是无限的。您可以使用称为“打结”的技术。使用此技术,图形表示为无限树:
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)
就这些。例如:
编辑
shortest
函数的实现是惰性的,因此shortest xs ys
以z1: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
,要么[]
或x:xs
),但是
减少到
either (Left . (1:)) (Right . (2:)) $ ...
shortest (path' p1) (shortest (path' p2) (path' p3))
这不在whnf中。这句话更进一步:
either (Left . (1:)) (Right . (2:)) $ either (Left . (2:)) (Right . (3:)) $ ...
等等,直到堆栈溢出
但是
减少到
either (Left . (1:)) (Right . (2:)) $ ...
shortest (path' p1) (shortest (path' p2) (path' p3))
如果
ps=[p1、p2、p3]
。所以最短的函数必须是惰性的,因为path'p2
和path'p3
在一个有循环的图中可以是无限的。如果将城市和航空公司分成两种数据类型:data Place=France | Greece | Singapore |曼谷| Bago | Yangon | New德里deriving(Eq,Show)
和数据航空公司=Lufthansa | BangkokAirways派生(Eq,Show
。然后您可以定义城市::地点->(地点,航空公司,地点)
,编译器可以警告您(使用-Wall
)当您错过模式匹配时。您需要的是最短路径算法,即Dijkstra算法。有一些图形库可以做到这一点,但如果您想自己编写,您可能应该从实现图形数据类型开始。如果您创建了城市和航空,编译器肯定会更高效、更容易帮助您将行分为两种数据类型:data Place=France | Greece | Singapore |曼谷| Bago | Yangon | New德里派生(Eq,Show)
和data Airline=Lufthansa | BangkokAirways派生(Eq,Show
。然后您可以定义城市::地点->(地点,航空公司,地点)
,编译器可以警告您(使用-Wall
)当您错过模式匹配时。您需要的是最短路径算法,即Dijkstra算法。有一些图形库可以做到这一点,但如果您想自己编写,您可能应该从实现图形数据类型开始。但是,这将如何处理字符串?因为这只适用于Char和IntTheshortest
function相当微妙。它肯定不是“返回最短列表”的最常见实现,因为它会仔细生成Thunk,以便打结过程正常。@chi,为什么不?shortest
是懒惰的,但不是异国情调。例如,此长度$take 10$shortest[1..][2..]
打印10