gpt4 book ai didi

clojure - 设置位置(x、y、基本方向)并允许在 clojure 中更新

转载 作者:行者123 更新时间:2023-12-05 01:24:13 25 4
gpt4 key购买 nike

所以我有一个问题想用 clojure 解决。这是我尝试用多种语言进行的编程练习,以“学习”这门语言。

这是我的第一个不可变函数式语言,我遇到了一些挑战。

练习从文件中读取一些“指令”,格式为PLACE X, Y, CARDINAL_DIRECTION(例如 PLACE 0,0,NORTH)

然后可以使用MOVE向前移动一格

播放器可以使用 LEFT、RIGHT 左右旋转。

最后 REPORT 将打印出玩家的 x、y 和主方向

我目前面临的挑战是我不确定将玩家位置存储在何处。在基于类的语言中,我会有一个保存坐标的玩家类。

以下是我目前的解决方案

(ns toyrobot.core
(use clojure.java.io))

(defstruct player :x :y :face)
(def ^:dyanmic robot nil)

(defn file->vec
"Read in a file from the resources directory"
[input]
(with-open [rdr (reader input)]
(into [] (line-seq rdr))))

(defn parse-command [line]
"Parse command"
(clojure.string/split line #"\s|,"))

(defn on-the-board? [co-ordinates]
"Check if the x & y co-ordinates are on the board"
(let [x (Integer/parseInt (first co-ordinates))
y (Integer/parseInt (second co-ordinates))]
(if (and (>= x 0) (>= y 0) (<= x 5) (<= y 5))
true
false)))

(defn place [co-ordinates]
"Place the robot at the co-ordinates"
(if (on-the-board? co-ordinates)
co-ordinates
nil))

(defn -main []
(doseq [command (file->vec "resources/input.txt")]
(case (clojure.string/upper-case (first (parse-command command)))
"PLACE" (place (rest (parse-command command)))
"MOVE" (println "move")
"LEFT" (println "left")
"RIGHT" (println "right")
"REPORT" (println "report")
"")))

最佳答案

Thumbnail's answer很好,我真的不想分心,但你的小练习也让我有机会玩弄 Clojure 的 lisp-nature,我无法抗拒。

;Agree that unit vectors are more convenient to work with than cardinal directions
(def north [0 1])
(def east [1 0])
(def south [0 -1])
(def west [-1 0])

;Just for a taste of macros
(defmacro name-value-map [& xs]
`(zipmap ~(mapv name xs) (vector ~@xs)))

(def direction->heading (name-value-map north east south west))
(def heading->direction (clojure.set/map-invert direction->heading))

;Robot commands just return an updated structure
(defn left [robot]
(update-in robot [:heading] (fn [[dx dy]] [(- 0 dy) dx])))

(defn right [robot]
(update-in robot [:heading] (fn [[dx dy]] [dy (- 0 dx)])))

(defn move [robot]
(update-in robot [:position] (partial mapv + (:heading robot))))

;Report and return unchanged
(defn report [robot]
(println "Robot at" (:position robot)
"facing" (heading->direction (:heading robot)))
robot)

;Create
(defn place [x y heading]
{:position [x y] :heading heading})

现在有了这些,您已经通过线程宏在迷你 DSL 中使用了您的语言

(-> (place 3, 3, north) report move report left report move report)
;Printed Output:
;Robot at [3 3] facing north
;Robot at [3 4] facing north
;Robot at [3 4] facing west
;Robot at [2 4] facing west
;Return Value:
;{:position [2 4], :heading [-1 0]}

确实,如果您有一个您信任的文件,其内容

(def sample-file-contents "(place 3, 3, north) move left move")

你可以以表格的形式读入数据

user=> (read-string (str "(" sample-file-contents ")"))
((place 3 3 north) move left move)

交错一些报告(在REPL *1是之前的值)

user=> (interleave *1 (repeat 'report))
((place 3 3 north) report move report left report move report)

处理线程宏

user=> (cons '-> *1)
(-> (place 3 3 north) report move report left report move report)

evaluate 与上面相同的输出

user=> (eval *1)
;Printed Output
;Robot at [3 3] facing north
;Robot at [3 4] facing north
;Robot at [3 4] facing west
;Robot at [2 4] facing west
;Return Value:
;{:position [2 4], :heading [-1 0]}

通过良好的解析库,更恰本地设置它不需要太多额外的努力

(require '[instaparse.core :as insta])
(require '[clojure.tools.reader.edn :as edn])

(def parse (insta/parser "
<commands> = place (ws command)*
<command> = place | unary-command
place = <'place '> location <', '> direction
unary-command = 'left' | 'right' | 'move' | 'report'
number = #'[0-9]+'
location = number <', '> number
direction = 'north' | 'east' | 'west' | 'south'
<ws> = <#'\\s+'> "))

(defn transform [parse-tree]
(insta/transform
{:number edn/read-string
:location vector
:direction direction->heading
:place (fn [[x y] heading] #(place x y heading))
:unary-command (comp resolve symbol)}
parse-tree))

(defn run-commands [[init-command & more-commands]]
(reduce (fn [robot command] (command robot)) (init-command) more-commands))

现在我们已经放弃了对括号的要求(并包含一些示例换行符),重新定义示例文件

(def sample-file-contents "place 3, 3, north report
move report
left report
move report")

并且,再次在 REPL 中显示中间结果

user=> (parse sample-file-contents)
([:place [:location [:number "3"] [:number "3"]] [:direction "north"]] [:unary-command "report"]
[:unary-command "move"] [:unary-command "report"]
[:unary-command "left"] [:unary-command "report"]
[:unary-command "move"] [:unary-command "report"])

user=> (transform *1)
(#< clojure.lang.AFunction$1@289f6ae> #'user/report
#'user/move #'user/report
#'user/left #'user/report
#'user/move #'user/report)

user=> (run-commands *1)
;Printed Output:
;Robot at [3 3] facing north
;Robot at [3 4] facing north
;Robot at [3 4] facing west
;Robot at [2 4] facing west
;Return Value:
;{:position [2 4], :heading [-1 0]}

关于clojure - 设置位置(x、y、基本方向)并允许在 clojure 中更新,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22090673/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com