小机器人行走
【题目】小机器人站还原点(0,0)位置,头朝北方。建立直角坐标系,x的正方向为东,y的正方向为北。现在给小机人发指令。指令只有3种:
“L” 左转90度
“R” 右转90度
“数字” 表示在当前方向行走x米
请编程,对一个指令序列,输出小机器人的最终位置。
从haskell 的角度看,当前位置和当前头的朝向就是一个初始状态。从这个初始状态开始,对指令序列进行折叠,得到最终的状态。
上代码:
data HeadDir = D北 | D东 | D南 | D西
type Pos = (Int, Int)
robotWalk :: [String] -> Pos
robotWalk xs = fst $ foldl f ((0,0),D北) xs
where
f (p,hd) s = case s of
"L" -> (p, turnL hd)
"R" -> (p, turnR hd)
dis -> (go p hd (read dis), hd)
turnL D北 = D西
turnL D西 = D南
turnL D南 = D东
turnL D东 = D北
turnR = turnL . turnL . turnL
go (x,y) hd dis = case hd of
D北 -> (x, y+dis)
D东 -> (x+dis, y)
D南 -> (x, y-dis)
D西 -> (x-dis, y)
main :: IO ()
main = do
s <- getContents
print . robotWalk . words $ s
还是老套路。当情况比较少的时候,枚举比分析规律更可靠些。
右转定义为左转 3 次,能偷懒就偷懒…
假设转的度数不是 90 度, 而是任意角度, 怎么办?
这时数学就能发挥作用了。可以用复数
表达机器人的位置。
用单位向量表达头的朝向。转向就是对单位向量的乘法。
行走就是对当前位置向量的加法运算…
上个复数版本的
import Data.Complex
robotWalk :: [String] -> Complex Double
robotWalk = f (0 :+ 0) (0 :+ 1)
where
f c位 _ [] = c位
f c位 c向 (x:xs) = case x of
"L" -> f c位 (c向 * i1) xs
"R" -> f c位 (c向 * i2) xs
dis -> let k = read dis :+ 0 in f (c位 + c向 * k) c向 xs
i1 = 0 :+ 1
i2 = 0 :+ (-1)
main :: IO ()
main = do
s <- getContents
print . robotWalk . words $ s
这个写法与头脑中的直观感觉更近些。