I made a Tycoon script for TI-84 Plus CE and I don't think it's really good.
I'm not that good at calculator programming and I don't know much. I just learned some from a friend and a couple YouTube videos.
I was wondering, does anyone know how I can improve this script to make it better?
Here's what I have:
Btw the -> is the STO key.
:ClrHome
:13->S
:5->T
:13->X
:5->Y
:0->B
:5->C
:1->D
:Output(T,S,"CASH:"
:Output(Y,X,"M"
:getkey->A
:X->S
:Y->T
:If A=26
:Then
:X+1->X
:If X=27
:26->x
:End
:If A=34
:Then
:Y+1->Y
:If Y=11
:10->Y
:End
:If A=25
:Then
:Y-1->Y
:If Y=1
:2->Y
:End
:If A=11
:Then
:Stop
:End
:If X=D and Y=C
:Then
:B+1->B
:Output(1,6,B
:End
:If B≥200
:Then
:6->E
:1->F
:Output(E,F,"5"
:End
:If X=F and Y=E and B≥200
:Then
:B+5->B
:Output(1,6,B
:End
:If B≥1500
:Then
:7->G
:1->H
:Output(G,H,"10"
:End
:If X=H and Y=G and B≥1500
:Then
:B+10->B
:Output(1,6,B
:End
:End
Future questions of this nature should be asked at codereview.SE, but I'll take a look at some optimizations for you anyway.
I will only be looking for optimizations in size, not speed (although they may make your program faster). Here are some:
First off, four of these lines can have their end quotes removed (-4)
0->B:5->C can be DelVar B5->C (you can remove the following colon) (-1)
You don't need a Then or End with your If statement if the condition is one line. So, :If A=11:Then:Stop:End can just be :If A=11:Stop (-4)
Now for the major optimizations I see:
First, and perhaps more obvious, is the fact that you call :Output(1,6,B a lot. It looks like you can just move this outside your If block, and remove the Then and End for -4 bytes each
Second, which you may not have known about, are the min( and max( commands, which return the smaller or larger of two values or of a list, respectively. Here's an example:
:If A=26
:Then
:X+1->X
:If X=27
:26->X
:End
The above code is equivalent to:
:If A=26
:min(26,X+1->X
It looks like you can use this optimization four times, which is significant because it makes the program smaller, faster, and more readable.
Overall, I hope I was able to help you. If you have any questions at all, please ask :)
Related
In my ongoing quest to recreate lodash in lisp as a way of getting familiar with the language I am trying to write a concat-list function that takes an initial list and an arbitrary number of additional lists and concatenates them.
I'm sure that this is just a measure of getting familiar with lisp convention, but right now my loop is just returning the second list in the argument list, which makes sense since it is the first item of other-lists.
Here's my non-working code (edit: refactored):
(defun concat-list (input-list &rest other-lists)
;; takes an arbitrary number of lists and merges them
(loop
for list in other-lists
append list into input-list
return input-list
)
)
Trying to run (concat-list '(this is list one) '(this is list two) '(this is list three)) and have it return (this is list one this is list two this is list three).
How can I spruce this up to return the final, merged list?
The signature of your function is a bit unfortunate, it becomes easier if you don't treat the first list specially.
The easy way:
(defun concat-lists (&rest lists)
(apply #'concatenate 'list lists))
A bit more lower level, using loop:
(defun concat-lists (&rest lists)
(loop :for list :in lists
:append list))
Going lower, using dolist:
(defun concat-lists (&rest lists)
(let ((result ()))
(dolist (list lists (reverse result))
(setf result (revappend list result)))))
Going even lower would maybe entail implementing revappend yourself.
It's actually good style in Lisp not to use LABELS based iteration, since a) it's basically a go-to like low-level iteration style and it's not everywhere supported. For example the ABCL implementation of Common Lisp on the JVM does not support TCO last I looked. Lisp has wonderful iteration facilities, which make the iteration intention clear:
CL-USER 217 > (defun my-append (&rest lists &aux result)
(dolist (list lists (nreverse result))
(dolist (item list)
(push item result))))
MY-APPEND
CL-USER 218 > (my-append '(1 2 3) '(4 5 6) '(7 8 9))
(1 2 3 4 5 6 7 8 9)
Some pedagogical solutions to this problem
If you just want to do this, then use append, or nconc (destructive), which are the functions which do it.
If you want to learn how do to it, then learning about loop is not how to do that, assuming you want to learn Lisp: (loop for list in ... append list) really teaches you nothing but how to write a crappy version of append using arguably the least-lispy part of CL (note I have nothing against loop & use it a lot, but if you want to learn lisp, learning loop is not how to do that).
Instead why not think about how you would write this if you did not have the tools to do it, in a Lispy way.
Well, here's how you might do that:
(defun append-lists (list &rest more-lists)
(labels ((append-loop (this more results)
(if (null this)
(if (null more)
(nreverse results)
(append-loop (first more) (rest more) results))
(append-loop (rest this) more (cons (first this) results)))))
(append-loop list more-lists '())))
There's a dirty trick here: I know that results is completely fresh so I am using nreverse to reverse it, which does so destructively. Can we write nreverse? Well, it's easy to write reverse, the non-destructive variant:
(defun reverse-nondestructively (list)
(labels ((r-loop (tail reversed)
(if (null tail)
reversed
(r-loop (rest tail) (cons (first tail) reversed)))))
(r-loop list '())))
And it turns out that a destructive reversing function is only a little harder:
(defun reverse-destructively (list)
(labels ((rd-loop (tail reversed)
(if (null tail)
reversed
(let ((rtail (rest tail)))
(setf (rest tail) reversed)
(rd-loop rtail tail)))))
(rd-loop list '())))
And you can check it works:
> (let ((l (make-list 1000 :initial-element 1)))
(time (reverse-destructively l))
(values))
Timing the evaluation of (reverse-destructively l)
User time = 0.000
System time = 0.000
Elapsed time = 0.000
Allocation = 0 bytes
0 Page faults
Why I think this is a good approach to learning Lisp
[This is a response to a couple of comments which I thought was worth adding to the answer: it is, of course, my opinion.]
I think that there are at least three different reasons for wanting to solve a particular problem in a particular language, and the approach you might want to take depends very much on what your reason is.
The first reason is because you want to get something done. In that case you want first of all to find out if it has been done already: if you want to do x and the language a built-in mechanism for doing x then use that. If x is more complicated but there is some standard or optional library which does it then use that. If there's another language you could use easily which does x then use that. Writing a program to solve the problem should be something you do only as a last resort.
The second reason is because you've fallen out of the end of the first reason, and you now find yourself needing to write a program. In that case what you want to do is use all of the tools the language provides in the best way to solve the problem, bearing in mind things like maintainability, performance and so on. In the case of CL, then if you have some problem which naturally involves looping, then, well, use loop if you want to. It doesn't matter whether loop is 'not lispy' or 'impure' or 'hacky': just do what you need to do to get the job done and make the code maintainable. If you want to print some list of objects, then by all means write (format t "~&~{~A~^, ~}~%" things).
The third reason is because you want to learn the language. Well, assuming you can program in some other language there are two approaches to doing this.
the first is to say 'I know how to do this thing (write loops, say) in languages I know – how do I do it in Lisp?', and then iterate this for all the thing you already know how to do in some other language;
the second is to say 'what is it that makes Lisp distinctive?' and try and understand those things.
These approaches result in very approaches to learning. In particular I think the first approach is often terrible: if the language you know is, say, Fortran, then you'll end up writing Fortran dressed up as Lisp. And, well, there are perfectly adequate Fortran compilers out there: why not use them? Even worse, you might completely miss important aspects of the language and end up writing horrors like
(defun sum-list (l)
(loop for i below (length l)
summing (nth i l)))
And you will end up thinking that Lisp is slow and pointless and return to the ranks of the heathen where you will spread such vile calumnies until, come the great day, the golden Lisp horde sweeps it all away. This has happened.
The second approach is to ask, well, what are the things that are interesting about Lisp? If you can program already, I think this is a much better approach to the first, because learning the interesting and distinctive features of a language first will help you understand, as quickly as possible, whether its a language you might actually want to know.
Well, there will inevitably be argument about what the interesting & distinctive features of Lisp are, but here's a possible, partial, set.
The language has a recursively-defined data structure (S expressions or sexprs) at its heart, which is used among other things to represent the source code of the language itself. This representation of the source is extremely low-commitment: there's nothing in the syntax of the language which says 'here's a block' or 'this is a conditiona' or 'this is a loop'. This low-commitment can make the language hard to read, but it has huge advantages.
Recursive processes are therefore inherently important and the language is good at expressing them. Some variants of the language take this to the extreme by noticing that iteration is simply a special case of recursion and have no iterative constructs at all (CL does not do this).
There are symbols, which are used as names for things both in the language itself and in programs written in the language (some variants take this more seriously than others: CL takes it very seriously).
There are macros. This really follows from the source code of the language being represented as sexprs and this structure having a very low commitment to what it means. Macros, in particular, are source-to-source transformations, with the source being represented as sexprs, written in the language itself: the macro language of Lisp is Lisp, without restriction. Macros allow the language itself to be seamlessly extended: solving problems in Lisp is done by designing a language in which the problem can be easily expressed and solved.
The end result of this is, I think two things:
recursion, in addition to and sometimes instead of iteration is an unusually important technique in Lisp;
in Lisp, programming means building a programming language.
So, in the answer above I've tried to give you examples of how you might think about solving problems involving a recursive data structure recursively: by defining a local function (append-loop) which then recursively calls itself to process the lists. As Rainer pointed out that's probably not a good way of solving this problem in Common Lisp as it tends to be hard to read and it also relies on the implementation to turn tail calls into iteration which is not garuanteed in CL. But, if your aim is to learn to think the way Lisp wants you to think, I think it is useful: there's a difference between code you might want to write for production use, and code you might want to read and write for pedagogical purposes: this is pedagogical code.
Indeed, it's worth looking at the other half of how Lisp might want you to think to solve problems like this: by extending the language. Let's say that you were programming in 1960, in a flavour of Lisp which has no iterative constructs other than GO TO. And let's say you wanted to process some list iteratively. Well, you might write this (this is in CL, so it is not very like programming in an ancient Lisp would be: in CL tagbody establishes a lexical environment in the body of which you can have tags – symbols – and then go will go to those tags):
(defun print-list-elements (l)
;; print the elements of a list, in order, using GO
(let* ((tail l)
(current (first tail)))
(tagbody
next
(if (null tail)
(go done)
(progn
(print current)
(setf tail (rest tail)
current (first tail))
(go next)))
done)))
And now:
> (print-list-elements '(1 2 3))
1
2
3
nil
Let's program like it's 1956!
So, well, let's say you don't like writing this sort of horror. Instead you'd like to be able to write something like this:
(defun print-list-elements (l)
;; print the elements of a list, in order, using GO
(do-list (e l)
(print e)))
Now if you were using most other languages you need to spend several weeks mucking around with the compiler to do this. But in Lisp you spend a few minutes writing this:
(defmacro do-list ((v l &optional (result-form nil)) &body forms)
;; Iterate over a list. May be buggy.
(let ((tailn (make-symbol "TAIL"))
(nextn (make-symbol "NEXT"))
(donen (make-symbol "DONE")))
`(let* ((,tailn ,l)
(,v (first ,tailn)))
(tagbody
,nextn
(if (null ,tailn)
(go ,donen)
(progn
,#forms
(setf ,tailn (rest ,tailn)
,v (first ,tailn))
(go ,nextn)))
,donen
,result-form))))
And now your language has an iteration construct which it previously did not have. (In real life this macro is called dolist).
And you can go further: given our do-list macro, let's see how we can collect things into a list:
(defun collect (thing)
;; global version: just signal an error
(declare (ignorable thing))
(error "not collecting"))
(defmacro collecting (&body forms)
;; Within the body of this macro, (collect x) will collect x into a
;; list, which is returned from the macro.
(let ((resultn (make-symbol "RESULT"))
(rtailn (make-symbol "RTAIL")))
`(let ((,resultn '())
(,rtailn nil))
(flet ((collect (thing)
(if ,rtailn
(setf (rest ,rtailn) (list thing)
,rtailn (rest ,rtailn))
(setf ,resultn (list thing)
,rtailn ,resultn))
thing))
,#forms)
,resultn)))
And now we can write the original append-lists function entirely in terms of constructs we've invented:
(defun append-lists (list &rest more-lists)
(collecting
(do-list (e list) (collect e))
(do-list (l more-lists)
(do-list (e l)
(collect e)))))
If that's not cool then nothing is.
In fact we can get even more carried away. My original answer above used labels to do iteration As Rainer has pointed out, this is not safe in CL since CL does not mandate TCO. I don't particularly care about that (I am happy to use only CL implementations which mandate TCO), but I do care about the problem that using labels this way is hard to read. Well, you can, of course, hide this in a macro:
(defmacro looping ((&rest bindings) &body forms)
;; A sort-of special-purpose named-let.
(multiple-value-bind (vars inits)
(loop for b in bindings
for var = (typecase b
(symbol b)
(cons (car b))
(t (error "~A is hopeless" b)))
for init = (etypecase b
(symbol nil)
(cons (unless (null (cddr b))
(error "malformed binding ~A" b))
(second b))
(t
(error "~A is hopeless" b)))
collect var into vars
collect init into inits
finally (return (values vars inits)))
`(labels ((next ,vars
,#forms))
(next ,#inits))))
And now:
(defun append-lists (list &rest more-lists)
(collecting
(looping ((tail list) (more more-lists))
(if (null tail)
(unless (null more)
(next (first more) (rest more)))
(progn
(collect (first tail))
(next (rest tail) more))))))
And, well, I just think it is astonishing that I get to use a programming language where you can do things like this.
Note that both collecting and looping are intentionally 'unhygenic': they introduce a binding (for collect and next respectively) which is visible to code in their bodies and which would shadow any other function definition of that name. That's fine, in fact, since that's their purpose.
This kind of iteration-as-recursion is certainly cool to think about, and as I've said I think it really helps you to think about how the language can work, which is my purpose here. Whether it leads to better code is a completely different question. Indeed there is a famous quote by Guy Steele from one of the 'lambda the ultimate ...' papers:
procedure calls may be usefully thought of as GOTO statements which also pass parameters
And that's a lovely quote, except that it cuts both ways: procedure calls, in a language which optimizes tail calls, are pretty much GOTO, and you can do almost all the horrors with them that you can do with GOTO. But GOTO is a problem, right? Well, it turns out so are procedure calls, for most of the same reasons.
So, pragmatically, even in a language (or implementation) where procedure calls do have all these nice characteristics, you end up wanting constructs which can express iteration and not recursion rather than both. So, for instance, Racket which, being a Scheme-family language, does mandate tail-call elimination, has a whole bunch of macros with names like for which do iteration.
And in Common Lisp, which does not mandate tail-call elimination but which does have GOTO, you also need to build macros to do iteration, in the spirit of my do-list above. And, of course, a bunch of people then get hopelessly carried away and the end point is a macro called loop: loop didn't exist (in its current form) in the first version of CL, and it was common at that time to simply obtain a copy of it from somewhere, and make sure it got loaded into the image. In other words, loop, with all its vast complexity, is just a macro which you can define in a CL which does not have it already.
OK, sorry, this is too long.
(loop for list in (cons '(1 2 3)
'((4 5 6) (7 8 9)))
append list)
Lisp s-expressions are a concise and flexible way to represent code as an abstract syntax tree. Relative to the more specialized data structures used by compilers for other languages, however, they have one drawback: it is difficult to keep track of the file and line number corresponding to any particular point in the code. At least some Lisps end up just punting the problem; in the event of an error, they report source location only as far as function name, not file and line number.
Some dialects of Scheme have solved the problem by representing code not with ordinary cons cells, but with syntax objects, which are isomorphic to cons cells but can also carry additional information such as source location.
Has any implementation of Common Lisp solved this problem? If so, how?
The Common Lisp standard says very little about these things. It mentions for example that the function ed may take a function name and then open the editor with respective source code. But there is no mechanism specified and this feature is entirely provided by the development environment, possibly in combination with the Lisp system.
A typical way to deal with that is to compile a file and the compiler will record the source location of the object defined (a function, a variable, a class, ...). The source location could for example be placed on the property list of the symbol (the name of the thing defined), or recorded in some other place. Also the actual source code as a list structure can be associated with a Lisp symbol. See the function FUNCTION-LAMBDA-EXPRESSION.
Some implementations do more sophisticated source location recording. For example LispWorks can locate a specific part of a function which is currently executed. It also notes when the definition comes from an editor or a Listener. See Dspecs: Tools for Handling Definitions. The debugger then can for example locate where the code of a certain stack frame is located in the source.
SBCL also has a feature to locate source code.
Notice also that the actual 'source code' in Common Lisp is not always a text a file, but the read s-expression. eval and compile - two standard functions - don't take strings or filenames as arguments. They use the actual expressions:
CL-USER 26 > (compile 'foo (lambda (x) (1+ x)))
FOO
NIL
NIL
CL-USER 27 > (foo 41)
42
S-expressions as code are not bound to any particular textual formatting. They can be reformatted by the pretty printer function pprint and this may take available width into account to generate a layout.
So, noting the structure maybe be useful and it would be less useful to record source lines.
My understanding is that whatever data Scheme stores in the AST is data that can be associated to expressions in a CL environment.
Scheme
(defun my-simple-scheme-reader (stream)
(let ((char (read-char stream)))
(or (position char "0123456789")
(and (member char '(#\newline #\space #\tab)) :space)
(case char
(#\) :closing-paren)
(#\( (loop
with beg = (file-position stream)
for x = (my-simple-scheme-reader stream)
until (eq x :closing-paren)
unless (eq x :space)
collect x into items
finally (return (list :beg beg
:end (file-position stream)
:items items))))))))
For example:
(with-input-from-string (in "(0(1 2 3) 4 5 (6 7))")
(my-simple-scheme-reader in))
returns:
(:BEG 1 :END 20 :ITEMS
(0 (:BEG 3 :END 9 :ITEMS (1 2 3)) 4 5 (:BEG 15 :END 19 :ITEMS (6 7))))
The enriched tree represents syntax objects.
Common-Lisp
(defun make-environment ()
(make-hash-table :test #'eq))
(defun my-simple-lisp-reader (stream environment)
(let ((char (read-char stream)))
(or (position char "0123456789")
(and (member char '(#\newline #\space #\tab)) :space)
(case char
(#\) :closing-paren)
(#\( (loop
with beg = (file-position stream)
for x = (my-simple-lisp-reader stream environment)
until (eq x :closing-paren)
unless (eq x :space)
collect x into items
finally
(setf (gethash items environment)
(list :beg beg :end (file-position stream)))
(return items)))))))
Test:
(let ((env (make-environment)))
(with-input-from-string (in "(0(1 2 3) 4 5 (6 7))")
(values
(my-simple-lisp-reader in env)
env)))
Returns two values:
(0 (1 2 3) 4 5 (6 7))
#<HASH-TABLE :TEST EQL :COUNT 3 {1010524CD3}>
Given a cons cell, you can track back its original position. You can add more precise information if you want to. Once you evaluate a defun, for example, the source information can be attached to the function object, or as a symbol property, which means the information is garbage collected on redefinitions.
Remark
Note that in both cases there is no source file to keep track of, unless the system is able to track back to the original string in the source file where the reader is called.
read-line and read-char both require you press Enter key after typing something. Is there any mechanism in Common Lisp that would allow the program to continue upon the press of any single character immediately, without requiring the additional step of pressing Enter?
I'm trying to build a quick, dynamic text input interface for a program so users can quickly navigate around and do different things by pressing numbers or letters corresponding to onscreen menus. All the extra presses of the Enter key seriously interrupt the workflow. This would also be similar to a "y/n" type of interrogation from a prompt, where just pressing "y" or "n" is sufficient.
I am using SBCL, if that makes a difference. Perhaps this is implementation specific, as I tried both examples on this page and it does not work (I still need to press Enter); here's the first one:
(defun y-or-n ()
(clear-input *standard-input*)
(loop as dum = (format t "Y or N for yes or no: ")
as c = (read-char)
as q = (and (not (equal c #\n)) (not (equal c #\y)))
when q do (format t "~%Need Y or N~%")
unless q return (if (equal c #\y) 'yes 'no)))
read-char doesn't require you to press enter. E.g.,
CL-USER> (with-input-from-string (x "hello")
(print (read-char x)))
#\h
Similarly, if you send some input into SBCL from the command line, it will be read without a newline:
$ echo -n hello | sbcl --eval "(print (read-char))"
…
#\h
After reading and printing #\h, SBCL saw the ello:
*
debugger invoked on a UNBOUND-VARIABLE in thread #<THREAD
"initial thread" RUNNING
{1002979011}>:
The variable ELLO is unbound.
I think this is enough to confirm that it's not that read-char needs a newline, but rather that the buffering of the input is the problem. I think this is the same problem (or non-problem) that's described in a comp.lang.lisp thread from 2008: Re: A problem with read-char. The user asks:
Is it possible to make read-char behave like getch in С when working
with interactive stream (standard-input)? In SBCL read-char wants
"enter" key to unhang from REPL, in C getchar returns immediately
after user press key on keyboard. Probably is possible to run code
that uses read-char with direct console access, aside REPL?
There were four responses (see the thread index to get to all of them). These explain why this behavior is observed (viz., that the Lisp process isn't getting raw input from the terminal, but rather buffered input). Pascal Bourguignon described the problem, and a way to handle this with CLISP (but doesn't provide all that much help, aside from the usual good advice) about working around this in SBCL:
The difference is that curses puts the terminal in raw mode to be able
to receive the characters from the keyboard one at a time, instead of
leaving the terminal in cooked mode, where the unix driver bufferize
lines and handles backspace, amongst other niceties.
…
Now, I don't know about SBCL, (check the manual of SBCL). I only have
the Implementation Notes of CLISP loaded in my wetware. In CLISP you
can use the EXT:WITH-KEYBOARD macro (while the basic output features
of curses are provided by the SCREEN package).
Rob Warnock's response included some workaround code for CMUCL that might or might not work for SBCL:
I once wrote the following for CMUCL for an application that wanted
to be able to type a single character response to a prompt without
messing up the terminal screen:
(defun read-char-no-echo-cbreak (&optional (stream *query-io*))
(with-alien ((old (struct termios))
(new (struct termios)))
(let ((e0 (unix-tcgetattr 0 old))
(e1 (unix-tcgetattr 0 new))
(bits (logior tty-icanon tty-echo tty-echoe
tty-echok tty-echonl)))
(declare (ignorable e0 e1)) ;[probably should test for error here]
(unwind-protect
(progn
(setf (slot new 'c-lflag) (logandc2 (slot old 'c-lflag) bits))
(setf (deref (slot new 'c-cc) vmin) 1)
(setf (deref (slot new 'c-cc) vtime) 0)
(unix-tcsetattr 0 tcsadrain new)
(read-char stream))
(unix-tcsetattr 0 tcsadrain old)))))
SBCL has probably diverged considerably from CMUCL in this area, but
something similar should be doable with SBCL. Start by looking in the
SB-UNIX or maybe the SB-POSIX packages...
User vippstar's response provided a link to what might be the most portable solution
Since you want to do something that might not be portable to a
microcontroller (but the benifit is the much more enhanced UI), use a
non-standard library, such as CL-ncurses.
Adding another answer to point out the existence of this tutorial: cl-charms crash course, by Daniel "jackdaniel" Kochmański. Disabling buffering is related to how the terminal is configured, and cl-charms is a library that exploit the ncurses C library to configure the terminal for interactive usage.
I found cl-charms, which seems to be a fork of the abandoned cl-curses. However, the included example program charms-paint uses 100 % CPU to run the trivial paint application. The problem seems to be that the main loop busy-waits for input.
You could use the trivial-raw-io library to read a single character without pressing Enter. Usage: (trivial-raw-io:read-char). It works on SBCL, CCL, CMUCL, and CLISP on Linux, and should work on other Unix-like operating systems as well. The library has only one simple dependency (the alexandria library), and it is licensed under the BSD 2-clause license.
I've struggled recently with the same issue and I ended up solving it simply by using FFI to interact with termios and disable canonical mode. Essentially that what's mentioned in #JoshuaTaylor's response. I couldn't get that code to work with SBCL, for whatever reason, so I made a few changes. Here is the complete working code (only tested with SBCL):
(define-alien-type nil
(struct termios
(c_iflag unsigned-long)
(c_oflag unsigned-long)
(c_cflag unsigned-long)
(c_lflag unsigned-long)
(c_cc (array unsigned-char 20))
(c_ispeed unsigned-long)
(c_ospeed unsigned-long)))
(declaim (inline tcgetattr))
(define-alien-routine "tcgetattr" int
(fd int)
(term (* (struct termios))))
(declaim (inline tcsetattr))
(define-alien-routine "tcsetattr" int
(fd int)
(action int)
(term (* (struct termios))))
(defun read-single-byte (&optional (s *standard-input*))
(with-alien ((old (struct termios))
(new (struct termios)))
(let ((e0 (tcgetattr 0 (addr old)))
(e1 (tcgetattr 0 (addr new)))
(n-lflag (slot new 'c_lflag)))
(declare (ignorable e0 e1))
(unwind-protect
(progn
(setf (ldb (byte 1 8) n-lflag) 0) ; disables canonical mode
(setf (ldb (byte 1 3) n-lflag) 0) ; disables echoing input char
(setf (slot new 'c_lflag) n-lflag)
(tcsetattr 0 0 (addr new))
(read-byte s))
(tcsetattr 0 0 (addr old))))))
Simply interfacing with termios should do the trick, there is no need for external libraries. You can find on termios's man page more information (https://man7.org/linux/man-pages/man3/termios.3.html), but essentially it's when the terminal is in canonical mode (ICANON) that it needs to wait for a line delimiter before the contents of the buffer become available. I hope this helps!
I tried using ctrl-c then :a
But It doesn't work here.
My code is like:
(defun game-repl()
(loop (print (eval (read)))))
then I run
(game-repl())
look()
(require 'cl)
(loop (setq x (read))
(if (eq x 'exit)
(return)
(print (eval x))))
Emacs modes often send an interruption signal to the inferior program only when you hit Ctrl-C twice in a row (i.e., the key sequence you are looking for is C-c C-c). In particular, this is true for SLIME.
This is because C-c is a prefix key that is usually combined with other keys to access a whole bunch of mode-specific features.
This question can refer to:
how to programmatically break out of a loop that would otherwise be infinite or
how to manually stop an infinite loop that's already raging.
The 1st has satisfactorily been answered by #fred-foo (and it seems it was OP's actual question). The 2nd has been tackled by #matthias-benkard but his answer is not working for emacs lisp.
The actual answer to manually stop a running infinite emacs-lisp loop is Ctrl+g (C-g in emacs-speak).
There is a documentation page in the emacs lisp manual on this very topic.
Sorry, I don't have the reputation to just amend #matthias-benkard's answer and the question ranks high on search engines...
[Reference http://www.psg.com/~dlamkins/sl/chapter05.html]
Most of the time you write a LOOP form, you'd like to have a way out. Fortunately, a RETURN form anywhere inside will cause control to leave the LOOP; any value you specify becomes the value of the LOOP form:
? (loop
(print "Here I am.")
(return 17)
(print "I never got here."))
"Here I am."
17
RETURN is normally used in a conditional form, like this:
? (let ((n 0))
(loop
(when (> n 10) (return))
(print n) (print (* n n))
(incf n)))
0 0
1 1
2 4
3 9
4 16
5 25
6 36
7 49
8 64
9 81
10 100
NIL
?
This Stack Exchange answer is the first hit on google for "break a never ending loop slime"
C-c C-b
What is missing, is that different lisps handle this break differently. I found this answer because GNU Clisp just doesn't intercept SLIME's C-c C-b. Nor does it do what SBCL does which is intercept both C-c C-b and C-c C-c.
I'm trying to write a number guessing game in Lisp as a time-killing project. However, when I try to load up the program using SBCL, I get the following error:
debugger invoked on a SB-C::INPUT-ERROR-IN-COMPILE-FILE in thread #<THREAD
"initial thread" RUNNING
{AA14959}>:
READ failure in COMPILE-FILE at character 477:
end of file on #<SB-SYS:FD-STREAM
for "file /home/andy/Dropbox/Programming/Common Lisp/number-game.lisp"
{B4F45F9}>
Type HELP for debugger help, or (SB-EXT:QUIT) to exit from SBCL.
restarts (invokable by number or by possibly-abbreviated name):
0: [CONTINUE] Ignore runtime option --load "number-game.lisp".
1: [ABORT ] Skip rest of --eval and --load options.
2: Skip to toplevel READ/EVAL/PRINT loop.
3: [QUIT ] Quit SBCL (calling #'QUIT, killing the process).
(SB-C::READ-FOR-COMPILE-FILE
#<SB-SYS:FD-STREAM
for "file /home/andy/Dropbox/Programming/Common Lisp/number-game.lisp"
{B4F45F9}>
477)
What does this error mean? The code is as follows, and the error appears when loading the file and calling (play) from the REPL:
;;;; number-game.lisp
;;;;
;;;; Andrew Levenson
;;;; 10/25/2010
;;;;
;;;; Simple number guessing game. User has
;;;; five guesses to determine a number between
;;;; one and one hundred, inclusive (1-100).
;;; Set global variable for the target number:
(defparameter *target* nil)
;;; Set the iterator so we may check the number of guesses
(defparameter *number-of-guesses* 0)
;;; Welcome the user
(defun welcome-user ()
(format t "Welcome to the number guessing game!~%"))
;;; Prompt for a guess
(defun prompt-for-guess ()
(format t "Please enter your guess (1-100): ")
(finish-output nil) ; nil directs finish-output to standard IO
(check-guess((read-guess)))
;;; Read in a guess
(defun read-guess ()
(let ((guess (read)))
(if (numberp guess) ; If true, return guess. Else, call prompt-for-guess
(progn
(setq *number-of-guesses* (+ *number-of-guesses* 1))
guess)
(prompt-for-guess))))
;;; Check if the guess is higher than, lower than, or equal to, the target
(defun check-guess (guess)
(if (equal guess *target*)
(equal-to)
(if (> guess *target*)
(greater-than (guess))
(if (< guess *target*)
(less-than (guess))))))
;;; If the guess is equal to the target, the game is over
(defun equal-to ()
(format t "Congratulations! You have guessed the target number, ~a!~%" *target*)
(y-or-n-p "Play again? [y/n] "))
;;; If the guess is greater than the target, inform the player.
(defun greater-than (guess)
(format t "Sorry, ~a is greater than the target.~%" guess)
(if (< *number-of-guesses* 6)
(prompt-for-guess)
(game-over)))
;;; If the guess is less than the target, inform the player.
(defun less-than (guess)
(format t "Sorry, ~a is less than the target.~%" guess)
(if (< *number-of-guesses* 6)
(prompt-for-guess)
(game-over)))
;;; If the player has run out of guesses, give them the option
;;; of playing the game again.
(defun game-over ()
(y-or-n-p "You have run out of guesses. Play again? [y/n] "))
;;; Play the game
(defun play ()
;; If it's their first time playing this session,
;; make sure to greet the user.
(unless (> *number-of-guesses* 0)
(welcome-user))
;; Reset their remaining guesses
(setq *number-of-guesses* 0)
;; Set the target value
(setq *target*
;; Random can return float values,
;; so we must round the result to get
;; an integer value.
(round
;; Add one to the result, because
;; (random 100) yields a number between
;; 0 and 99, whereas we want a number
;; from 1 to 100 inclusive.
(+ (random 100) 1)))
(if (equal (prompt-for-guess) "y")
(play)
(quit)))
(I'm fairly certain that the program doesn't work minus that one error, I'm still a complete novice when it comes to Lisp. This is just the first error I've encountered that I can't figure out on my own.)
Oh, and the issue most likely has to do with the prompt-for-guess, read-guess and check-guess functions, because those were the ones I was messing with when this error cropped up.
It looks like you didn't close enough parens on your prompt-for-guess defun.
The reader is getting to the end of the file and noticing that it has a form still open, and can't figure out where it's from.
An easy way I use to find errors like this is to have my text editor indent the region, and make sure everything lines up like I think it should.
The command in Emacs is M-x check-parens (it checks everything that needs to balance, like quotes, as well). It can be a bit mystifying if everything balances, because it does nothing in that case.
check-parens
Command: Check for unbalanced parentheses in the current buffer.
More accurately, check the narrowed part of the buffer for unbalanced
expressions ("sexps") in general. This is done according to the
current syntax table and will find unbalanced brackets or quotes as
appropriate. (See Info node `(emacs)Parentheses'.) If imbalance is
found, an error is signaled and point is left at the first unbalanced
character.
end of file during read, there is a closing parenthesis (or similar) missing. Character 477. Move the cursor in your text to 477 and check which expression it is.
Check your IDE for a command to find unbalanced expressions.
In LispWorks this would be M-x Find Unbalanced Parentheses.
SLIME should have some command for that, too.