How to read just two integers from file in common lisp - file-io

(defun read-file (filename)
(with-open-file (stream filename)
(loop for line = (read-line stream nil)
while line
collect line)
)
)
I'm totally new to lisp so I want to read integer by integer but I have this line by line piece of code.
So I couldn't find that.
For exmp my file;
10 20
I need help .thx

Not sure exactly what you're asking, so here's a short proposition to read a file into a string, split it by whitespace, and parse each number with parse-integer:
(mapcar #'parse-integer (str:words (uiop:read-file-string "foo.txt")))
uiop comes from ASDF and is included in all major implementations, str is a library to quickload.
uiop also has read-file-lines.

OP goal seems to be to read lines from a file, each line containing a pair of integers, and to return a list containing all of the integers read from the file.
Given an input file, numbers.dat:
10 20
30 40
50 60
70 80
90 100
the file can be read into a list using read-line:
CL-USER> (with-open-file (in "numbers.dat" :direction :input)
(loop :for line := (read-line in nil)
:while line
:collect line))
("10 20" "30 40" "50 60" "70 80" "90 100" "")
But now the list contains strings, and each string corresponds to a pair of integers. We need a function to extract the integers from the strings. Common Lisp has the function read-from-string, which parses a string containing a printed representation of an object and returns that object and the first unread position of the input string. This function can be used in a loop to extract the integers from an input string:
CL-USER> (loop :with num
:and pos := 0
:do (setf (values num pos)
(read-from-string "10 20" t nil :start pos))
:collect num
:until (= pos 5))
(10 20)
If this code is removed to a function that can handle strings of varying lengths and empty strings, and mapped over an input list like ("10 20" "30 40" "50 60" "70 80" "90 100" ""), we will be close to the goal with ((10 20) (30 40) (50 60) (70 80) (90 100) ()). The contents of such a list could be appended together to obtain the desired list of all integers from the file:
;;; Expects well-behaved input
(defun get-numbers-from-string (string)
(if (string= string "") ; empty string returns empty list
'()
(let ((*read-eval* nil) ; small precaution to guard against malicious input
(length (length string)))
(loop :with num ; number from STRING
:and pos := 0 ; next position in STRING to read from
:do (setf (values num pos)
(read-from-string string t nil :start pos))
:collect num
:until (= pos length)))))
(defun read-integer-pairs (filename)
(with-open-file (in filename
:direction :input)
(apply #'append ; combine sublists into a single list of numbers
(mapcar #'get-numbers-from-string ; transform strings to number pairs
(loop :for line := (read-line in nil)
:while line
:collect line)))))
Sample interaction:
CL-USER> (read-integer-pairs "numbers.dat")
(10 20 30 40 50 60 70 80 90 100)

"Read just 2 integers from a file"
leaves some room for interpretation.
I will start by answering in the most literal sense:
Really just 2 integers in the file
(defun read-2-integers (stream)
(let ((i0 (read stream nil))
(i1 (read stream nil)))
(list i0 i1)))
If you write your functions in terms of a stream instead of a filename, you have an easier time testing interactively. For example:
CL-USER> (with-input-from-string (stream "10 20")
(read-2-integers stream))
(10 20)
2 integers per line in the file
Here is, where your code in the question comes in - as you would want
to read the file line by line (in case there are so many lines, that the whole file might not fit into memory or just because of other reasons).
(defun read-lines-with-2-integers (stream)
(loop
for line = (read-line stream nil)
while line
collecting (with-input-from-string (line-stream line)
(read-2-integers line-stream))))
Let's test if it works:
CL-USER> (with-input-from-string (stream "10 20
30 40
50 60")
(read-lines-with-2-integers stream))
((10 20) (30 40) (50 60))
Lines with integers in the file
;; first, we replace our function "read-2-integers" with something a bit more general, so we can apply it to a single line:
(defun read-integers (stream)
(loop
for c = (peek-char t stream nil) ;; skips whitespace
while c
collecting (read stream nil)))
;; next, we use this function just like we used "read-2-integers" before:
(defun read-lines-with-integers (stream)
(loop
for line = (read-line stream nil)
while line
collecting
(with-input-from-string (line-stream line)
(read-integers line-stream))))
which we can test like so:
CL-USER> (with-input-from-string (stream "10 20
30 40 50
60 70
80")
(read-lines-with-integers stream))
((10 20) (30 40 50) (60 70) (80))
File with integers - lines do not matter
In this case, we can just use our read-integers function from above:
CL-USER> (with-input-from-string (stream "10
20
30 40")
(read-integers stream))
(10 20 30 40)

Related

Why is my test.check test using inputs smaller than the generator?

I've implemented my own version of index-of. It takes two strings and returns the index where the second string is found in the first. In use, it seems to work fine, but it's failing the test.check property test. The weird thing is test.check says it fails at values like "0bU", but I have it set so the smallest input is 100 characters. Why is it using inputs smaller than what I've told it to use?
I wrote my own generator using gen/fmap and it looks correct when I call it using gen/sample.
The code:
(defn gen-alphanumeric "A generator that accepts a minimum and maximum string length" [min max]
(gen/fmap str/join (gen/vector gen/char-alphanumeric min max)))
(defspec indexof-functionality-test
(prop/for-all [s1 (gen-alphanumeric 100 200)
begin gen/nat 80]
(let [end (+ begin 10)
s2 (subs s1 begin end)]
(= (str/index-of s1 s2) (cp-str/indexof s1 s2)))))
The output from lein test
FAIL in (indexof-functionality-test) (string_test.clj:57) expected:
{:result true} actual: {:shrunk {:total-nodes-visited 11, :depth 4,
:pass? false, :result false, :result-data nil, :time-shrinking-ms 2,
:smallest ["240"]}, :failed-after-ms 0, :num-tests 4, :seed
1676231835751, :fail ["n5n0"], :result false, :result-data nil,
:failing-size 3, :pass? false, :test-var "indexof-functionality-test"}
EDIT:
My cp-str/indexof
(defn indexof
"given two strings, returns the index where sb begins in sa. Nil if not found"
([sa sb]
(indexof sa sb 0))
([sa sb i]
(let [sa-len (count sa)
sb-len (count sb)]
(cond
(>= 0 sa-len) nil
(>= 0 sb-len) nil
; this is hit when element not found
(> (+ i sb-len) sa-len) nil
(= (subs sa i (+ i sb-len)) sb) i
:else (indexof sa sb (inc i))))))
EDIT2: It fails on one computer and passes on another. Those three methods are identical on the two computers.

Split lines in clojure while reading from file

I am learning clojure at school and I have an exam coming up. I was just working on a few things to make sure I get the hang of it.
I am trying to read from a file line by line and as I do, I want to split the line whenever there is a ";".
Here is my code so far
(defn readFile []
(map (fn [line] (clojure.string/split line #";"))
(with-open [rdr (reader "C:/Users/Rohil/Documents/work.txt.txt")]
(doseq [line (line-seq rdr)]
(clojure.string/split line #";")
(println line)))))
When I do this, I still get the output:
"I;Am;A;String;"
Am I missing something?
I'm not sure if you need this at school, but since Gary already gave an excellent answer, consider this as a bonus.
You can do elegant transformations on lines of text with transducers. The ingredient you need is something that allows you to treat the lines as a reducible collection and which closes the reader when you're done reducing:
(defn lines-reducible [^BufferedReader rdr]
(reify clojure.lang.IReduceInit
(reduce [this f init]
(try
(loop [state init]
(if (reduced? state)
#state
(if-let [line (.readLine rdr)]
(recur (f state line))
state)))
(finally
(.close rdr))))))
Now you're able to do the following, given input work.txt:
I;am;a;string
Next;line;please
Count the length of each 'split'
(require '[clojure.string :as str])
(require '[clojure.java.io :as io])
(into []
(comp
(mapcat #(str/split % #";"))
(map count))
(lines-reducible (io/reader "/tmp/work.txt")))
;;=> [1 2 1 6 4 4 6]
Sum the length of all 'splits'
(transduce
(comp
(mapcat #(str/split % #";"))
(map count))
+
(lines-reducible (io/reader "/tmp/work.txt")))
;;=> 24
Sum the length of all words until we find a word that is longer than 5
(transduce
(comp
(mapcat #(str/split % #";"))
(map count))
(fn
([] 0)
([sum] sum)
([sum l]
(if (> l 5)
(reduced sum)
(+ sum l))))
(lines-reducible (io/reader "/tmp/work.txt")))
or with take-while:
(transduce
(comp
(mapcat #(str/split % #";"))
(map count)
(take-while #(> 5 %)))
+
(lines-reducible (io/reader "/tmp/work.txt")))
Read https://tech.grammarly.com/blog/building-etl-pipelines-with-clojure for more details.
TL;DR embrace the REPL and embrace immutability
Your question was "what am I missing?" and to that I'd say you're missing one of the best features of Clojure, the REPL.
Edit: you might also be missing that Clojure uses immutable data structures so
consider this code snippet:
(doseq [x [1 2 3]]
(inc x)
(prn x))
This code does not print "2 3 4"
it prints "1 2 3" because x isn't a mutable variable.
During the first iteration (inc x) gets called, returns 2, and that gets thrown away because it wasn't passed to anything, then (prn x) prints the value of x which is still 1.
Now consider this code snippet:
(doseq [x [1 2 3]] (prn (inc x)))
During the first iteration the inc passes its return value to prn so you get 2
Long example:
I don't want to rob you of the opportunity to solve the problem yourself so I'll use a different problem as an example.
Given the file "birds.txt"
with the data "1chicken\n 2duck\n 3Larry"
you want to write a function that takes a file and returns a sequence of bird names
Lets break this problem down into smaller chunks:
first lets read the file and split it up into lines
(slurp "birds.txt") will give us the whole file a string
clojure.string/split-lines will give us a collection with each line as an element in the collection
(clojure.string/split-lines (slurp "birds.txt")) gets us ["1chicken" "2duck" "3Larry"]
At this point we could map some function over that collection to strip out the number like (map #(clojure.string/replace % #"\d" "") birds-collection)
or we could just move that step up the pipeline when the whole file is one string.
Now that we have all of our pieces we can put them together in a functional pipeline where the result of one piece feeds into the next
In Clojure there is a nice macro to make this more readable, the -> macro
It takes the result of one computation and injects it as the first argument to the next
so our pipeline looks like this:
(-> "C:/birds.txt"
slurp
(clojure.string/replace #"\d" "")
clojure.string/split-lines)
last note on style, for Clojure functions you want to stick to kebab case so readFile should be read-file
I would keep it simple, and code it like this:
(ns tst.demo.core
(:use tupelo.test)
(:require [tupelo.core :as t]
[clojure.string :as str] ))
(def text
"I;am;a;line;
This;is;another;one
Followed;by;this;")
(def tmp-file-name "/tmp/lines.txt")
(dotest
(spit tmp-file-name text) ; write it to a tmp file
(let [lines (str/split-lines (slurp tmp-file-name))
result (for [line lines]
(for [word (str/split line #";")]
(str/trim word)))
result-flat (flatten result)]
(is= result
[["I" "am" "a" "line"]
["This" "is" "another" "one"]
["Followed" "by" "this"]])
Notice that result is a doubly-nested (2D) matrix of words. The simplest way to undo this is the flatten function to produce result-flat:
(is= result-flat
["I" "am" "a" "line" "This" "is" "another" "one" "Followed" "by" "this"])))
You could also use apply concat as in:
(is= (apply concat result) result-flat)
If you want to avoid building up a 2D matrix in the first place, you can use a generator function (a la Python) via lazy-gen and yield from the Tupelo library:
(dotest
(spit tmp-file-name text) ; write it to a tmp file
(let [lines (str/split-lines (slurp tmp-file-name))
result (t/lazy-gen
(doseq [line lines]
(let [words (str/split line #";")]
(doseq [word words]
(t/yield (str/trim word))))))]
(is= result
["I" "am" "a" "line" "This" "is" "another" "one" "Followed" "by" "this"])))
In this case, lazy-gen creates the generator function.
Notice that for has been replaced with doseq, and the yield function places each word into the output lazy sequence.

How to run a test file in SBCL

I am trying to run a test file in SBCL by executing the command sbcl --load file.lisp. However, when I execute the command the file is loaded, but I can't see my program output.
By the way here is a example of a test file:
(locally
(declare #+sbcl(sb-ext:muffle-conditions style-warning))
(handler-bind
(#+sbcl(style-warning #'muffle-warning))
(load "load.lisp")
))
(interval 5)
(interval 20)
(interval 500)
The load.lisp file, loads the source code of my program, that contains the definitions of several functions, including the interval function.
I already try other option from sbcl such as run sbcl --script file.lisp but the output is the same.
Anybody can help me with this problem? Thanks in advance.
** PRINT-OBJECT METHOD **
(defmethod print-object ((tensor tensor) stream)
"Implementation of the generic method print-object for the tensor data structure.
If the tensor is a vector, prints its elements separated by a whitespace.
If the tensor is not one of the previous cases, then for each sub-tensor of the
first dimension, prints the sub-tensor separated from the next sub-tensor by a
number of empty lines that is equal to the number of dimensions minus one."
(labels ((rec (arg last-iteration)
(cond ((null arg) nil)
((atom (car arg))
(format stream
(if (null (cdr arg)) "~A" "~A ")
(car arg))
(rec (cdr arg) nil))
((and (listp (car arg)) (null (cdr arg)))
(rec (car arg) last-iteration)
(unless last-iteration
(format stream "~%")))
(t
(rec (car arg) nil)
(format stream "~%")
(rec (cdr arg) last-iteration)))))
(rec (tensor-content tensor) t)))
When you load a file the return values of the forms are not automatically printed.
One option out of many:
(defun show (&rest items)
(dolist (i items)
(prin1 i)
(fresh-line))
(finish-output))
(locally
(declare #+sbcl(sb-ext:muffle-conditions style-warning))
(handler-bind
(#+sbcl(style-warning #'muffle-warning))
(load "load.lisp")
))
(show
(interval 5)
(interval 20)
(interval 500))
Should be usable with sbcl --script file.lisp.

Scheme Help - File Statistics

So I have to finish a project in Scheme and I'm pretty stuck. Basically, what the program does is open a file and output the statistics. Right now I am able to count the number of characters, but I also need to count the number of lines and words. I'm just trying to tackle this situation for now but eventually I also have to take in two files - the first being a text file, like a book. The second will be a list of words, I have to count how many times those words appear in the first file. Obviously I'll have to work with lists but I would love some help on where to being. Here is the code that I have so far (and works)
(define filestats
(lambda (srcf wordcount linecount charcount )
(if (eof-object? (peek-char srcf ) )
(begin
(close-port srcf)
(display linecount)
(display " ")
(display wordcount)
(display " ")
(display charcount)
(newline) ()
)
(begin
(read-char srcf)
(filestats srcf 0 0 (+ charcount 1))
)
)
)
)
(define filestatistics
(lambda (src)
(let ((file (open-input-file src)))
(filestats file 0 0 0)
)
)
)
How about 'tokenizing' the file into a list of lines, where a line is a list of words, and a word is a list of characters.
(define (tokenize file)
(with-input-from-file file
(lambda ()
(let reading ((lines '()) (words '()) (chars '()))
(let ((char (read-char)))
(if (eof-object? char)
(reverse lines)
(case char
((#\newline) (reading (cons (reverse (cons (reverse chars) words)) lines) '() '()))
((#\space) (reading lines (cons (reverse chars) words) '()))
(else (reading lines words (cons char chars))))))))))
once you've done this, the rest is trivial.
> (tokenize "foo.data")
(((#\a #\b #\c) (#\d #\e #\f))
((#\1 #\2 #\3) (#\x #\y #\z)))
The word count algorithm using Scheme has been explained before in Stack Overflow, for example in here (scroll up to the top of the page to see an equivalent program in C):
(define (word-count input-port)
(let loop ((c (read-char input-port))
(nl 0)
(nw 0)
(nc 0)
(state 'out))
(cond ((eof-object? c)
(printf "nl: ~s, nw: ~s, nc: ~s\n" nl nw nc))
((char=? c #\newline)
(loop (read-char input-port) (add1 nl) nw (add1 nc) 'out))
((char-whitespace? c)
(loop (read-char input-port) nl nw (add1 nc) 'out))
((eq? state 'out)
(loop (read-char input-port) nl (add1 nw) (add1 nc) 'in))
(else
(loop (read-char input-port) nl nw (add1 nc) state)))))
The procedure receives an input port as a parameter, so it's possible to apply it to, say, a file. Notice that for counting words and lines you'll need to test if the current char is either a new line character or a white space character. And an extra flag (called state in the code) is needed for keeping track of the start/end of a new word.

How to format a number with padding in Erlang

I need to pad the output of an integer to a given length.
For example, with a length of 4 digits, the output of the integer 4 is "0004" instead of "4". How can I do this in Erlang?
adding a bit of explanation to Zed's answer:
Erlang Format specification is: ~F.P.PadModC.
"~4..0B~n" translates to:
~F. = ~4. (Field width of 4)
P. = . (no Precision specified)
Pad = 0 (Pad with zeroes)
Mod = (no control sequence Modifier specified)
C = B (Control sequence B = integer in default base 10)
and ~n is new line.
io:format("~4..0B~n", [Num]).
string:right(integer_to_list(4), 4, $0).
The problem with io:format is that if your integer doesn't fit, you get asterisks:
> io:format("~4..0B~n", [1234]).
1234
> io:format("~4..0B~n", [12345]).
****
The problem with string:right is that it throws away the characters that don't fit:
> string:right(integer_to_list(1234), 4, $0).
"1234"
> string:right(integer_to_list(12345), 4, $0).
"2345"
I haven't found a library module that behaves as I would expect (i.e. print my number even if it doesn't fit into the padding), so I wrote my own formatting function:
%%------------------------------------------------------------------------------
%% #doc Format an integer with a padding of zeroes
%% #end
%%------------------------------------------------------------------------------
-spec format_with_padding(Number :: integer(),
Padding :: integer()) -> iodata().
format_with_padding(Number, Padding) when Number < 0 ->
[$- | format_with_padding(-Number, Padding - 1)];
format_with_padding(Number, Padding) ->
NumberStr = integer_to_list(Number),
ZeroesNeeded = max(Padding - length(NumberStr), 0),
[lists:duplicate(ZeroesNeeded, $0), NumberStr].
(You can use iolist_to_binary/1 to convert the result to binary, or you can use lists:flatten(io_lib:format("~s", [Result])) to convert it to a list.)
Eshell V12.0.3 (abort with ^G)
1> F = fun(Max, I)-> case Max - length(integer_to_list(I)) of X when X > 0 -> string:chars($0, X) ++ integer_to_list(I); _ -> I end end.
#Fun<erl_eval.43.40011524>
2> F(10, 22).
"0000000022"
3> F(3, 22345).
22345