Here's the source for the site: https://github.com/blakedietz/plott.id
I want you to know that this is the most useful comment I've ever gotten.
I had no idea that clojure.edn/read-string simply reads a string as literal clojure data.
This is opposed to clojure.core/read-string which reads a string as executable clojure code.
The edn namespace is for Extensible Data Notation[2], and exports functions for reading an object from a stream and from a string without passing it to the lisp reader.[3]
[1] https://clojuredocs.org/clojure.core/read-string [2] https://clojuredocs.org/clojure.edn [3] https://clojure.github.io/clojure/clojure.edn-api.html
Three factors may make a difference between using the two aforementioned functions. First, performance can matter if the program is doing enough I/O. Secondly, the machinery needed to evaluate language expressions will have to be available at runtime, meaning that the executable will have to be interpreted by an interpreter capable of doing the evaluation (which Lisp, Python, etc. are—they have REPLs after all) or the executable in languages without a runtime interpreter will have to be linked statically or dynamically with code that does the evaluation, making it resulting executable larger. Third, there is the really important reason that programs should avoid reading and evaluating their inputs as expressions: security.
A process runs code within a certain security context, generally with the same privileges as the user running the program. If the data input to the process can run expressions not found in the code, the program’s author can make few security guarantees about the results of running the program. Programs may receive input from the network and may even run at elevated security levels (e.g. setuid); these programs should not evaluate arbitrary input. For example, many security vulnerabilities come from database programs reading strings that are passed directly to the SQL interpreter. See the XKCD “Exploits of a Mom” [1].
I have some generic coding advice though. With slightly better names you can get rid of a lot of the comments. There is a lot of repetition in the code. If you factor that out, you will have even less need for comments. I highly recommend watching some Kevlin Henney talks on such topics, for example https://youtu.be/ZsHMHukIlJY?t=487 (but he has even better talks recently)
The input of the program should be supplied in a more format more natural to humans and let the computer do the transformation into whatever data structure it's comfortable with. So I would propose a simple, multi-line text as an input:
xxxxxx
0x000x
x*0x0x
xxxx00
00000x
xxxx0x
Then the code would look like this: (ns maze.solver
(:require [clojure.string :as str]))
(def maze-filename "maze.txt")
(defn parse [maze-str]
(->> maze-str
str/trim
str/split-lines
(mapv (partial into []))))
(def maze (-> maze-filename slurp parse))
You can also just hardwire a maze during development into the source file, since Clojure supports multiline strings: (def maze (parse "
xxxxxx
0x000x
x*0x0x
xxxx00
00000x
xxxx0x
"))
The result of `parse` function will contain character data types instead of 1 character long strings. I think in this case it's actually more readable and more concise too: [[\x \x \x \x \x \x]
[\0 \x \0 \0 \0 \x]
[\x \* \0 \x \0 \x]
[\x \x \x \x \0 \0]
[\0 \0 \0 \0 \0 \x]
[\x \x \x \x \0 \x]]
Next `println` actually accepts multiple arguments and concatenates them with a space, after stringifying them, so you can drop the extra `(str ...)` wrapping around them.Then again, the comment is superfluous. It's obvious that you are printing stuff. If you really want to tell what is it, just wrap it in a well-named function. Eg:
(defn report [paths]
(println "The maze has" (count paths) "paths.")
(println "The shortest path in the maze is:" (count (first paths)) "steps long.")
(println "The path is" (first paths))
(println "The longest path in the maze is:" (count (last paths)) "steps long.")
(println "The path is" (last paths)))
So you can just put `(report sorted-paths)` in your `-main`.To tell you the truth I didn't really know about this algorithm.
I just went at the problem function by function and this is what I ended up with.
My next idea is to "prerender" a series of interconnected waypoints across the map at the start and then pathfind to the nearest one before moving long distances using small bursts of pathfinding to move around obstacles and stay on track.
https://github.com/Torvaney/flow-solver
Although I used a much lazier strategy for doing the solving (reduction to SAT).
Really cool project!
https://github.com/clojure/clojure/blob/clojure-1.9.0/src/cl...
You only see it in cases where some functions are mutually recursive.
Lisp isn't an 'interpreted language'.
> have to be interpreted by an interpreter capable of doing the evaluation (which Lisp, Python, etc. are—they have REPLs after all)
SBCL, a Common Lisp implementation...
* (mapcar #'disassemble (list (lambda (a) (print a))))
; disassembly for (LAMBDA (A))
; Size: 28 bytes. Origin: #x226B7580
; 80: 498B4510 MOV RAX, [R13+16] ; no-arg-parsing entry point
; thread.binding-stack-pointer
; 84: 488945F8 MOV [RBP-8], RAX
; 88: 488BD3 MOV RDX, RBX
; 8B: B902000000 MOV ECX, 2
; 90: FF7508 PUSH QWORD PTR [RBP+8]
; 93: B8D8C35222 MOV EAX, #x2252C3D8 ; #<FDEFN PRINT>
; 98: FFE0 JMP RAX
; 9A: CC0F BREAK 15 ; Invalid argument count trap
(NIL)
To me that looks like machine code...A bunch of Lisp implementations use compilers for evaluation in the Read-Eval-Print-Loop.
Compilers for Lisp have been around in some form for many years—I can remember hearing Patrick Winston talking about them when I first learned Lisp in his class in 1973. There are both interpreters and compilers for many languages and translators that are in between that use JIT compilation at run time.
Lisp and it’s offshoots (Scheme, Dylan, Common Lisp, Clojure, etc.) have a wider range of compilation and interpretation strategies used in their implementations than other programming languages. Perhaps this is due to the fact that Lisp has been around longer than almost any other important language or perhaps its due to its homoiconicity.
However my point was really about the availability of eval at run time, however it is implemented, and particularly eval that runs in the program’s address space and security context.
It’s true that the SBCL implementation of Common Lisp, by default, compiles eval’s arguments at runtime, but it may also use interpretation at runtime depending on the value of the a global variable. Other implementations of Common Lisp always use interpretation, like CLISP. Also, Common Lisp has eval-when which affects the compile vs interpret decision [1].
Readers interested in interpretation vs compilation can find introductory coverage of the topic in Wikipedia, see [2]. Lisp has an interesting and important history to computer science as the first interpreted high level language, see it’s Wikipedia entry at [3].
[1] http://www.lispworks.com/documentation/lw50/CLHS/Body/s_eval...
[2] https://en.m.wikipedia.org/wiki/Interpreter_(computing)
[3] https://en.m.wikipedia.org/wiki/Lisp_(programming_language)
Sure, just categorizing Lisp as an interpreted languages isn't correct.
> However my point was really about the availability of eval at run time, however it is implemented, and particularly eval that runs in the program’s address space and security context.
Right, but technically Common Lisp doesn't requite eval to use a compiler and it can also load compiled code from the file system, at runtime.
> Other implementations of Common Lisp always use interpretation, like CLISP.
CLISP also provides a virtual machine with a byte code compiler (also optionally a JIT native code compiler), though the compiler needs to be called explicitly.
> Also, Common Lisp has eval-when which affects the compile vs interpret decision [1].
Depends. In an implementation like SBCL or CCL, which use compilation for EVAL, it has no effect on such a decision.