freenode/#lisp - IRC Chatlog
Search
15:09:22
minion
The URL https://gitlab.common-lisp.net/users/sign_in?secret=60ec7dbd will be valid until 15:15 UTC.
15:20:41
minion
The URL https://gitlab.common-lisp.net/users/sign_in?secret=edcbb93f will be valid until 15:30 UTC.
21:06:47
edgar-rft
not really clever code -> (read-from-string (concatenate 'string "(list " "1 20 30" ")"))
21:10:38
aeth
asdf_asdf_asdf: A string is a vector of characters, so it really *is* just a fancy way of saying something similar to #(#\1 #\Space #\2 #\0 #\Space #\3 #\0) which is why you get something like that in list form when you coerce it to a list
21:11:06
aeth
asdf_asdf_asdf: if you want something more robust, and your input is *only* integers, then use parse-integer in a loop
21:11:47
aeth
That will tell you that it could parse the integer up to position 3, and it parsed 123. Then you have to keep iterating.
21:13:15
aeth
If your input is something else, then you'll need something even fancier, and edgar-rft's hack solution looks better the more work you'd have to do
21:14:52
aeth
As an alternative to calling parse-integer with :junk-allowed, you could manually scan for the space separator and use parse-integer with :start and :end keyword arguments. If you use a more general parser from a library, you'd probably have to do this.
21:16:24
aeth
asdf_asdf_asdf: an example of this second form, which can easily be turned into a loop: (let ((string "7 20 30") (start 0)) (parse-integer string :start start :end (position #\Space string :start start))) => (values 7 1)
21:17:55
aeth
For something more general than an integer with parse-integer, you'll need another parser, ideally from a library, with the only requirement that it also needs to support :start and :end (any good one should). Alternatively, you can use something like cl-ppcre or split-sequence to split the string on #\Space instead of tracking the start position.
21:18:39
aeth
(And if it's just a throwaway script, there's nothing wrong with using edgar-rft's much simpler, but inelegant, solution)
21:27:35
aeth
Oh, I should also add that read-from-string is itself one such parser that takes a :start and a :end, so you could use the function edgar-rft used, but with my position-iterative or string-splitting approach instead of wrapping it in "(list " and ")", but it is probably too powerful for the given task.
21:35:46
akoana
aeth: would it be a good idea to (setf *read-eval* nil) before using read-from-string?
21:45:23
aeth
asdf_asdf_asdf: instead of using nth-value and calling it twice, you can use multiple-value-bind like this: (multiple-value-bind (number position) (parse-integer ...))
21:48:40
aeth
so you put the number you want to collect at the end, e.g. (multiple-value-bind (number position) (parse-integer ...) ... number)
22:05:21
fengshaun
how do I reset the symbols slime has loaded with slime-eval-buffer? I'm changing code and re-eval-ing, but now slime refuses to redefine my structs
22:07:15
aeth
asdf_asdf_asdf: I think that you can combine the do and the collect and get rid of the do, although you'll have to be careful that things are evaluated in the correct order
22:07:22
asdf_asdf_asdf
Is other manner to ignore argument from multiple-value-bind? I must use (declare (ignore arg))? How skip argument without (declare (ignore ...?
22:08:19
aeth
I think you can just get rid of the collect, replace the word "do" with "collect" and put j as your last line, and it should collect
22:08:47
aeth
(I'm not 100% sure without evaluating it because loops can often have ordering issues)
22:09:21
aeth
collect will collect the return value, so you don't really need both a do and a collect... you can have as complicated of a collect as you want
22:12:26
akoana
aeth: so this would be a safer variant of edgar-rft's proposal: https://termbin.com/ay89 - I've you care to have a look at :)
22:14:21
aeth
asdf_asdf_asdf: in case I was unclear, s/collect (parse-integer a :junk-allowed t :start junk1)// s/(declare (ignore j))// s/do/collect/ s/(setf junk1 n-v)/(setf junk1 n-v) j/ and it works (of course, you'd want better code formatting than that)
22:16:01
aeth
asdf_asdf_asdf: just replace "do" with "collect", you don't need an additional collect. collect will gladly do side effects like do, and the collect part only cares about the final value, which can be what you called j
22:16:17
aeth
then you don't need to ignore j, you can put j as the last position in the multiple-value-bind
22:19:10
akoana
edgar-rft: I really like your solution, it is short and why not just use the lisp reader :)
22:21:06
aeth
akoana: yes, that works. alternatively, within the *read-eval* you can use a simple loop: (loop :for end := (position #\Space string) :then (position #\Space string :start (1+ start)) :for start := 0 :then end :while end :collect (read-from-string string t nil :start end))
22:21:19
aeth
akoana: that doesn't work if it contains no spaces, though, so you'd have to special case that
22:24:58
aeth
you can fix the case of it containing no spaces by doing something like (or (position ...) (length string)) or (or (position ...) (- (length string) 1)) depending on which side of the off-by-one error it's on, but I messed up the actual range as well. I think the gist is clear, though
22:25:59
aeth
akoana: yes, that works, except you'll want a newline in front of the j so that it's clearer that it's a separate line. I just couldn't do that due to the restriction of IRC
22:27:56
aeth
asdf_asdf_asdf: the immediate issue is just one of style... just put a newline in front of "j" so that it's clear that that's being returned in the multiple-value-bind
22:28:13
aeth
asdf_asdf_asdf: in general, though, your style is incorrect, read a style guide, or use a tool to format the style for you
22:31:32
akoana
aeth: why is ":for" preferred over "for" as loop keyword? I'm quite new to lisp, so looking at other peoples code there are both variants, maybe concerning namespaces or Emacs syntax highlighting?
22:34:21
aeth
akoana: I use :for so it stands out as a keyword in syntax highlighting (and just visually in general), yes. It also doesn't polute a local package with variables like "for". It is a minority style, though.
22:35:12
aeth
akoana, asdf_asdf_asdf: The colon before keywords in loops are because loop will accept any symbol, including keyword symbols, so it's valid
22:35:57
aeth
it's also a nice play on "keyword" since the symbols in loops like that are keywords in the sense of C++ "keywords" while :foo symbols are "keywords" in the sense of CL vocabulary
22:37:31
aeth
asdf_asdf_asdf: Your style is much better now. Your loop still isn't quite indented correctly, but it's hard for tools to do that. A loop is a special case. In a loop like that, the collect should be lined up with the for, moving all of the indentation over a bit. It's best just to let Emacs handle this.
22:39:22
akoana
asdf_asdf_asdf: even though I'm an old vi addict, I use Emacs for lisp exclusively and the formatting is one of the many good reasons why.
22:41:10
akoana
aeth: I very thankful for the advices given here, as a newbee need that like water :)
22:51:15
aeth
akoana: assuming one space (otherwise you have to scan for the next start point) I almost have the loop ready... I'm just getting a bug.
22:51:41
aeth
(loop :for start := 0 :then (if end (1+ end) nil) :for end := (if start (position #\Space string :start start) nil) :while start :collect (read-from-string string t nil :start start :end (or end (length string))))
22:55:14
akoana
aeth: I hopelessy messed up the code completly, could you please paste the complete example somewhere?
23:04:10
aeth
I hope that's correct. Quite a few off by one errors, so it took me longer than it should.
23:05:24
aeth
At the last second, I moved my read-eval binding to be closer to the actual read call, but the original might be more efficient depending on the implementation
23:05:42
aeth
this just makes it easier to not accidentally rebind *read-eval* in one of the functions you call in the loop before the collect
23:11:43
aeth
asdf_asdf_asdf: that's also an alternative solution to your problem... you can also replace the LET and READ-FROM-STRING in the collect with a parse-integer where you only care about the first value (since end is being set in the iteration itself)...
23:11:59
aeth
i.e. it should work with the collect part being (parse-integer string :start start :end (or end (length string)))
23:18:23
akoana
aeth: so we could also optionally (defun parse-and-split-string (string &optional (separator #\space) debug)
23:24:07
akoana
aeth: we soon will have a nice replacement for split-sequence, cl-ppcre:split or uiop:split-string :)
23:25:52
akoana
aeth: seriously, I like that very much, for understanding and not to be forced to use any "external" library, I use lisp also on the raspberry pi, so it is easier to able to use clisp "standalone"
23:25:52
aeth
akoana: I mean, that's really it, isn't it? Add a no-op that doesn't parse at all, and add an alternate code path for a vector result instead of a list result, and now you have yet another split-sequence.
23:27:14
aeth
akoana: using the higher order function version: (parse-and-split-string "123 23487 39" (lambda (seq &key start end) (subseq seq start end))) => ("123" "23487" "39")
23:58:24
aeth
asdf_asdf_asdf: even if you don't use alexandria, the answer is in alexandria: https://gitlab.common-lisp.net/alexandria/alexandria/blob/3b849bc0116ea70f215ee6b2fbf354e862aaa9dd/sequences.lisp#L62-80
0:01:24
aeth
asdf_asdf_asdf: it is easy! two things. First, if you only care about a positive number, you only have to look at rotate-tail-to-head. Second, if you only care about lists, you only have to look at the first branch in rotate-tail-to-head, which are lines 31-37
0:03:06
aeth
oh, and it has a proper-list-length, but you can probably just replace that with length since you don't care about as many edge cases as alexandria...
0:25:16
remexre
Is there something already existing for printing objects with a subset of their slots?
0:26:15
remexre
this'd print a foo with a bar slot of 1 as #<FOO :BAR 1> or something, with pretty-printing and all that jazz
0:33:58
dlowe
(defmethod print-object ((object foo) stream) (format stream "this is my foo object: ~a" foo))
0:34:56
dlowe
there is also a print-unreadable-object macro for easily producing a print-object output in a standardized manner
0:36:50
aeth
dlowe: uh, two issues with that first method: one you said foo instead of object in the print... and two you mae a recursive print that made my SBCL unhappy
0:37:21
aeth
you probably just wanted this (defmethod print-object ((object foo) stream) (format stream "this is my foo object")) ; test with (make-instance 'foo)
0:38:15
aeth
you probably always want "print-unreadable-object" like this: (defmethod print-object ((object foo) stream) (print-unreadable-object (object stream :type t :identity t)))
0:38:46
dlowe
you know, I think I made that recursive print mistake the first time I made a print-object method too
0:39:33
aeth
(defmethod print-object ((object foo) stream) (print-unreadable-object (object stream :type t) (format stream "~A" 42))) ; pretend 42 is an accessor on foo, it isn't here so you can test it with (defclass foo () ()) and (make-instance 'foo)
0:40:26
aeth
if it's too verbose you can write a macro that hides all of that except for the "~A" 42 part and maybe optionally :identity t
0:41:04
remexre
I'm trying to abstract that all down to (define-print-object CLASS-NAME (&rest SLOTS))
0:42:48
aeth
remexre: (defmacro define-print-object (class (&key (type t) identity) &body body) `(defmethod print-object ((object ,class) stream) (print-unreadable-object (object stream :type ,type :identity ,identity) (format stream ,@body))))
0:43:45
aeth
remexre: if you *just* want to get it down to the slots, then you can abstract it a bit more, for instance, generate a FORMAT for every object in body (easier than generating a more advanced FORMAT string)
0:44:21
remexre
the body's what I want to generate though (largely because I don't wanna have to internalize the pretty printer)
0:45:31
aeth
remexre: right, so then process body to generate a (format stream "~A" ,foo) for every foo in body, and do a bit more additional processing if you don't want to have to do a slot access or accessor call in the body, either
0:46:00
aeth
then it'd wind up looking like (define-print-object foo () a b c) ; for slots or accessors a b and c, depending on which way you go
0:46:17
remexre
the "a bit more processing" seems non-trivial when pretty printing and such get involved
0:46:38
remexre
or at a minimum, I can't figure out how to get the pretty-printer to do what I want :P
0:55:39
aeth
remexre: personally I'd do it manually with the version that I provided, if I even wrote a macro. something like this: (define-print-object foo () "~A ~A ~A ~A" :foobar (foobar foo) :quux (quux foo)) ; modify the macro to say (,class ,class) instead of (object ,class) so you can do this
0:57:39
aeth
That then should say #<FOO :FOOBAR whatever :QUUX whatever> or #<foo :foobar whatever :quux whatever> depending on your *print-case*
0:59:12
aeth
If FORMAT doesn't do what you want, you're going to have to play with other print/write functions on the stream stream
0:59:46
remexre
(let ((big (loop for i upto 20 collect i))) (print-unreadable-object ((make-instance 'foo) *standard-output* :type t) (format t "~s ~a ~s ~a" :bar big :baz big)))
1:02:22
edgar-rft
akoana: the particular problem with my (read-from-string ...) hack is that it makes it too easy to inject malicious code into a running Lisp program. It's always better to check user data from the outside world via parse-integer or some other appropriate way first.
1:02:26
remexre
really I want https://homepages.inf.ed.ac.uk/wadler/papers/prettier/prettier.pdf for common lisp :P
1:03:38
aeth
akoana, edgar-rft: And *read-eval* isn't the only issue so it's best to use a library that is a known-safe read
1:06:13
aeth
remexre: Well, ~% will get you a newline but it still might not quite give you want you want. You might be able to solve it in one format string but it's like regex... you might not be able to read the line when you're done.
1:07:15
remexre
yeah, I'm doing this in the process of throwing away a format string I don't remember writing (committed at 5AM, sooooo) that's mostly punctuation
1:07:41
remexre
and ideally instead having loads of write, pprint-indent, pprint-newline, etc. calls
1:08:00
aeth
well, I've never really messed with the pprint stuff, I mostly just mix write-foo and format
1:09:48
remexre
yeah, I'm getting dangerously close to the point where I write my own implementation of the paper I linked above :P