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和IntThe
shortest
function相当微妙。它肯定不是“返回最短列表”的最常见实现,因为它会仔细生成Thunk,以便打结过程正常。@chi,为什么不?
shortest
是懒惰的,但不是异国情调。例如,此
长度$take 10$shortest[1..][2..]
打印
10