Confused about Common LISP `loop for var =` syntax - loops

I checked the post here on how to loop over a file. I checked several places online trying to understand what is going on in that code snipped posted here again:
(defun get-file (filename)
(with-open-file (stream filename)
(loop for line = (read-line stream nil)
while line
collect line)))
This syntax seems to be so arcane, and the references are not trivial to understand. According to the reference here " the fourth of seven for/as syntaxes", the syntax is:
for var [type-spec] = expr1 [then expr2]
And if there is no expr2, then expr1 is used.
What is confusing me:
I am lost at the lines without knowing what to ask other than how does it work:
while line
collect line
I wouldn't know how to substitute that with other code. It does not look like a list at all. My intuition would tell me that it should've looked something like:
(while line (collect line))
Also, isn't that also same as:
while line collect line
And what is the expr1:
(read-line stream nil)
while line
collect line)
or
while line
collect line
What if I had much more code instead of collect line? Will there be no lists? I don't see the structure.
I know these are more than 1 question, but probably there is something big I am missing, preventing me from asking a good question.

It's a loop. Stuff which runs multiple times.
Basically the loop has two parts:
part one defines a variable line and what happens to it: on each loop iteration line is set to the result of evaluating the expression (read-line stream nil). This means that lines get read one by one.
the second part are the clauses which are run on each iteration. This part says: as long as the variable line is not nil collect the value of line into a list.
Thus each line is read and as long there is a line, collect it into a list. If the end of the input stream (which is a file stream) is reached, the (read-line stream nil) form returns nil, the while sees that line is nil. nil is false and thus the loop is terminated. Then the so far collected lines will be returned as the result.
The whole loop form returns the collected list of lines.
Here loop is convenient because:
it conditionally collects a value
we don't need to give the result list a variable name, because there is only one accumulation going on: the single collect ... clause.
the result list is returned in the correct order: first collected item is the first element in the returned list
Background
Generally LOOP is a macro, which is based on the similar FOR macro from Interlisp of the early 70s. There the idea was introduced to have program constructs which could be used like normal english: for item in list collect item. This was called conversational Lisp. Similar ideas are for example used in Applescript, a simple scripting language from Apple. This style was never very popular in Lisp (though there are other macros using it), but the loop macro remained, because it turned out to be useful.

My intuition would tell me that it should've looked something like:
(while line (collect line))
So you might like Shinmera's FOR macro: https://github.com/Shinmera/for
The minimum one need to know is for… over, where over is generic, it works with many data types.
(for:for ((a over '(1 2 3))
(b over #(a b c))
(c over (for:for ((table as (make-hash-table)))
(setf (gethash (random 10) table) (random 10)) (repeat 3)))
(d over *package*)
(e over *random-state*)
(f over (directory (merge-pathnames "*.*" (user-homedir-pathname))))
(g over (make-string-input-stream "Hi!")))
(print (list a b c d e f g)))

Related

Prolog - load 2D array from text file into program

I'm currently working on a Prolog program which would, logically, have some kind of "save/load" feature. I've gotten the save part to work, where I'm now (as a start) creating three *.txt files, which will contain a 2D array/list each. However, I'm facing some issues trying to load it back into the program.
What I have right now is something as simple as:
% Initialize globals
?- nb_setval(aisles_global, []).
% Load all previously saved data from the given .txt files
load_all():-
exists_file('C:\\Users\\Xariez\\Desktop\\aisles.txt'),
open('C:\\Users\\Xariez\\Desktop\\aisles.txt', read, InAisles),
read_line_to_codes(InAisles, AisleString),
% read_line_to_string(InAisles, AisleString),
writeln(AisleString),
nb_setval(aisles_global, AisleString),
close(InAisles).
As previously mentioned, the files will have a 2D array each, but as an example:
aisles.txt
[["Beer", "Cider" ], [ "Milk", "Juice" ], ["Light Bread", "Dark Bread"]]
I've tried using both read_line_to_codes/2 and read_line_to_string/2. While it technically works when reading it into codes, I feel like it would quickly become annoying to reconstruct a 2D list/array since it's now got every character as a code. And while reading into a string succeeds in the reading part, we now have a string that LOOKS like a list, but isn't really one (if I've understood this situation correctly?). And hence I'm here.
If anyone got any ideas/help, that'd be greatly appreciated. Thanks!
Prolog has predicates for doing input/output of terms directly. You don't need to roll these yourself. Reading terms is done using read, while for writing there are several options.
Your best shot for writing is probably write_canonical, which will write terms in "canonical" syntax. This means that everything is quoted as needed (for example, an atom 'A' will be printed as 'A' and not as plain A like write would print it), and terms with operators are printed in prefix syntax, which means you get the same term even if the reader doesn't have the same operators declared (for example, x is y is printed as is(x, y)).
So you can write your output like:
dump(Aisles, Filename) :-
open(Filename, write, OutAisles),
write_canonical(OutAisles, Aisles),
write(OutAisles, '.'),
close(OutAisles).
Writing the . is necessary because read expects to read a term terminated by a period. Your reading predicate could be:
load(Aisles, Filename) :-
open(Filename, read, InAisles),
read(InAisles, Aisles),
close(InAisles).
Running this using some example data:
?- aisles(As), dump(As, aisles).
As = [["Beer", "Cider"], x is y, 'A', _G1380, ["Milk", "Juice"], ["Light Bread", "Dark Bread"]].
?- load(As, aisles).
As = [["Beer", "Cider"], x is y, 'A', _G1338, ["Milk", "Juice"], ["Light Bread", "Dark Bread"]].
The contents of the file, as you can check in a text editor, is:
[["Beer","Cider"],is(x,y),'A',_,["Milk","Juice"],["Light Bread","Dark Bread"]].
Note the canonical syntax for is. You should almost certainly avoid writing variables, but this shouldn't be a problem in your case.

After a reading a clojure file line-by-line how to fetch a particular string from particular line?

I am new to clojure. I am having trouble as in how to print a particular string after removing special characters and reading a clojure file line-by-line.
I have a file named example.clj which looks like:
(defn patient
"Some string"
(measures
patient/symptom-fever
patient/symptom-cold
.
.
.
.
patient/symptom-bp))
From the above file i have removed all the special characters and reading the file line-by-line with below clojure code.
(defn loadcljfile []
(def newstring(clojure.string/replace (slurp "C:/Users/Desktop/example.clj") #"[\[/.(:)\]-]" " "))
(with-open [rdr (reader newstring)]
(doseq [line (line-seq rdr)]
(println line))))
(loadcljfile)
Now not getting how to print patient i.e my defn name and all the measures.
Can anyone help me?
Thanks
when defining things with names inside a function it's much safer and easier to use let rather than def. def defines it for the whole namespace (usually the whole file) and let gives something a name just until it's closing ).
I'm this case it makes a lot of sense to let Clojure read the expression and turn it into a data structure for you. you can do this with the load-file function if your file is a function you wish to run or use tools.reader if you just want to have it at a data structure.
if you go the load-file approach you can print it with the clojure.repl/source function and if you go the tools.reader path then you can just print it.

Fortran. Keep reading first word of the document until it matches the input.

Good evening!
I am trying to read a text document in Fortran 95 and do some calculations with the values in it. The document has numerous gas names and certain values of 'A' assigned to them. So essentially it look something like this:
Document 1: gas values.
GAS A1 A2
steam 1 2
air 3 4
I want then the user to input a gas name (variable gasNameIn) and implement while loop to keep searching for the gas until it matches the input. So eg. user inputs 'air' and the program starts reading first words until air comes up. It then read values of A1 and A2 and uses them for calculation. What I did for it is that I opened the file as unit 25 and tried the following loop:
do while(gasName .NE. gasNameIn)
read(25, *) gasName
if (gasName .EQ. gasNameIn)
read(25,*) A1, A2
endif
enddo
but I get an error "End of file on unit 25".
Any ideas on how my while loop is wrong? Thank you!
By the first read statement, you read the name correctly, but Fortran then proceeds to the next line (record). Try to use
read(25, *, advance='no') gasName
If your searched gas was on the last line, you get the end of file error. Otherwise you will have an error when reading A1.
you need to read whole lines as strings and process. This is untested but the jist of it:
character*100 wholeline
wholeline=''
do while(index(wholeline,'air').eq.0)
read(unit,'(a)')wholeline
end do
then if you can count on the the first strings taking up ~7 cols like in the example,
read(wholeline(7:),*)ia1,ia2
What happened is that you read the whole line in as "gasName" and tested the whole line to see if it was equivalent to "gasNameIn". It never will be the same if the data is laid out the way you have in your sample, so you will get to the end of your file before you ever get a match. It has been a while since I've written in Fortran, but in the first loop "gasName" will be undefined. Usually that is a no no in programming.
The answer that got in before I could get mine typed in is the way forward with your problem. Give it a go. If you still have some trouble maybe I'll fire up a Fortran compiler and try my hand at Fortran again. (It's been since the early 90's that I've done any.)
CHEERS!
Hi, here is your code modified a bit, you can pitch what you don't need. I just added a couple of lines to make it a self-contained program. For instance, There is a statement which assigns the value "steam" to "gasNameIn". I think you mentioned that you would have the user enter a value for that. I've also added a "write" statement to show that your program has properly read the values. It is a crude kind of "unit test". Otherwise, I've only addressed the part of the program that your question asked about. Check this out:
character (len=5) :: gasName, gasNameIn
gasNameIn = "steam"
open (unit = 25, file = "gasvalues.txt")
do while (gasName .NE. gasNameIn)
read (25, *) gasName
if (gasName .EQ. gasNameIn) then
backspace (25)
read (25,*) gasName, A1, A2
write (*, *) "The gas is: ", gasName, ". The factors are A1: ", A1, "; A2: ", A2
endif
end do
close (25)
end
Ok, here are a couple of notes to help clarify what I've done. I tried to keep your code as intact as I could manage. In your program there were some things that my fortran 95 compiler complained about namely: the "then" is required in the "if" test, and "enddo" needed to be "end do". I think those were the main compiler issues I had.
As far as your code goes, when your program tests that "gasName" is the same as "gasNameIn", then you want to "backup" to that record again so that you can re-read the line. There are probably better ways to do this, but I tried to keep your program ideas the way you wanted them. Oh, yes, one more thing, I've named the input file "gasvalues.txt".
I hope this helps some.
CHEERS!

Writing drakma http-request array to file

What I want to do:
take the output of (drakma:http-request "someURL.jpg") and write it to a file. The output consists of a byte array.
What I came up with:
(with-open-file (my-stream "thing.jpg"
:direction :output
:element-type 'binary
:if-does-not-exist :create
:if-exists :supersede)
(let ((content (drakma:http-request "someURL.jpg")))
(loop for i across content do
(write-byte i my-stream))))
The error I get:
debugger invoked on a SIMPLE-ERROR in thread #<THREAD "initial thread" RUNNING
{1002978FB1}>:
could not find any output routine for :FULL buffered BINARY
I use SBCL 1.0.49.
If you want any other info or find my question unclear then please, ask away :).
EDIT:
The problem is the :element-type, it's supposed to be 'unsigned-byte.
You can find information on the different possibilities of :element-type over at the Common Lisp Hyper Spec under open . Other than that everything is correct.
PS. I couldn't respond as an answer because my reputation is too low.
As Xach said, you're better off using '(unsigned-byte 8) (or make a type-definition , e.g. (deftype binary () '(unsigned-byte 8))).
In addition, you can probably replace your loop over the array and writing byte by byte with a call to write-sequence

Read a very large text file into a list in clojure

What is the best way to read a very large file (like a text file having 100 000 names one on each line) into a list (lazily - loading it as needed) in clojure?
Basically I need to do all sorts of string searches on these items (I do it with grep and reg ex in shell scripts now).
I tried adding '( at the beginning and ) at the end but apparently this method (loading a static?/constant list, has a size limitation for some reason.
There are various ways of doing this, depending on exactly what you want.
If you have a function that you want to apply to each line in a file, you can use code similar to Abhinav's answer:
(with-open [rdr ...]
(doall (map function (line-seq rdr))))
This has the advantage that the file is opened, processed, and closed as quickly as possible, but forces the entire file to be consumed at once.
If you want to delay processing of the file you might be tempted to return the lines, but this won't work:
(map function ; broken!!!
(with-open [rdr ...]
(line-seq rdr)))
because the file is closed when with-open returns, which is before you lazily process the file.
One way around this is to pull the entire file into memory with slurp:
(map function (slurp filename))
That has an obvious disadvantage - memory use - but guarantees that you don't leave the file open.
An alternative is to leave the file open until you get to the end of the read, while generating a lazy sequence:
(ns ...
(:use clojure.test))
(defn stream-consumer [stream]
(println "read" (count stream) "lines"))
(defn broken-open [file]
(with-open [rdr (clojure.java.io/reader file)]
(line-seq rdr)))
(defn lazy-open [file]
(defn helper [rdr]
(lazy-seq
(if-let [line (.readLine rdr)]
(cons line (helper rdr))
(do (.close rdr) (println "closed") nil))))
(lazy-seq
(do (println "opening")
(helper (clojure.java.io/reader file)))))
(deftest test-open
(try
(stream-consumer (broken-open "/etc/passwd"))
(catch RuntimeException e
(println "caught " e)))
(let [stream (lazy-open "/etc/passwd")]
(println "have stream")
(stream-consumer stream)))
(run-tests)
Which prints:
caught #<RuntimeException java.lang.RuntimeException: java.io.IOException: Stream closed>
have stream
opening
closed
read 29 lines
Showing that the file wasn't even opened until it was needed.
This last approach has the advantage that you can process the stream of data "elsewhere" without keeping everything in memory, but it also has an important disadvantage - the file is not closed until the end of the stream is read. If you are not careful you may open many files in parallel, or even forget to close them (by not reading the stream completely).
The best choice depends on the circumstances - it's a trade-off between lazy evaluation and limited system resources.
PS: Is lazy-open defined somewhere in the libraries? I arrived at this question trying to find such a function and ended up writing my own, as above.
Andrew's solution worked well for me, but nested defns are not so idiomatic, and you don't need to do lazy-seq twice: here is an updated version without the extra prints and using letfn:
(defn lazy-file-lines [file]
(letfn [(helper [rdr]
(lazy-seq
(if-let [line (.readLine rdr)]
(cons line (helper rdr))
(do (.close rdr) nil))))]
(helper (clojure.java.io/reader file))))
(count (lazy-file-lines "/tmp/massive-file.txt"))
;=> <a large integer>
You need to use line-seq. An example from clojuredocs:
;; Count lines of a file (loses head):
user=> (with-open [rdr (clojure.java.io/reader "/etc/passwd")]
(count (line-seq rdr)))
But with a lazy list of strings, you cannot do those operations efficiently which require the whole list to be present, like sorting. If you can implement your operations as filter or map then you can consume the list lazily. Otherwise it'll be better to use an embedded database.
Also note that you should not hold on to the head of the list, otherwise the whole list will be loaded in memory.
Furthermore, if you need to do more than one operation, you'll need to read the file again and again. Be warned, laziness can make things difficult sometimes.
see my answer here
(ns user
(:require [clojure.core.async :as async :refer :all
:exclude [map into reduce merge partition partition-by take]]))
(defn read-dir [dir]
(let [directory (clojure.java.io/file dir)
files (filter #(.isFile %) (file-seq directory))
ch (chan)]
(go
(doseq [file files]
(with-open [rdr (clojure.java.io/reader file)]
(doseq [line (line-seq rdr)]
(>! ch line))))
(close! ch))
ch))
so:
(def aa "D:\\Users\\input")
(let [ch (read-dir aa)]
(loop []
(when-let [line (<!! ch )]
(println line)
(recur))))
You might find the iota library useful for working with very large files in Clojure. I use iota sequences all the time when I am applying reducers to large amounts of input, and iota/vec provides random access to files larger than memory by indexing them.

Resources