Path: ...!eternal-september.org!feeder3.eternal-september.org!news.eternal-september.org!.POSTED!not-for-mail From: Kaz Kylheku <643-408-1753@kylheku.com> Newsgroups: comp.lang.lisp Subject: Re: Given string 'a.bc.' -- replace each dot(.) with 0 or 1 Date: Sun, 19 May 2024 19:56:35 -0000 (UTC) Organization: A noiseless patient Spider Lines: 161 Message-ID: <20240519121441.337@kylheku.com> References: <20240518140005.921@kylheku.com> Injection-Date: Sun, 19 May 2024 21:56:36 +0200 (CEST) Injection-Info: dont-email.me; posting-host="51aa926811f3eee5d2771c560ded4f5e"; logging-data="3731370"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX19mXczEYIwKr8QuAf6jpxG6gdiFD2T1jCc=" User-Agent: slrn/pre1.0.4-9 (Linux) Cancel-Lock: sha1:RCQwxwJUfv6EgdcxGhPnA2+HBAw= Bytes: 6237 On 2024-05-18, Kaz Kylheku <643-408-1753@kylheku.com> wrote: > On 2024-05-18, HenHanna wrote: >> >> How can i write this function simply? (in Common Lisp) >> >> -- Given a string 'a.bc.' -- replace each dot(.) with 0 or 1. >> >> -- So the value is a list of 4 strings: >> ('a0bc0' 'a0bc1' 'a1bc0' 'a1bc1') >> >> -- The order is not important. >> If the string has 3 dots, the value is a list of length 8. >> >> If the program is going to be simpler, >> pls use, e.g. (a $ b c $) rather than 'a.bc.' > > TXR Lisp: > > (defun bindots (str) > (let* ((s (copy str)) > (ixs (where (op eql #\.) s)) > (n (len ixs))) > (collect-each ((digs (rperm '(#\0 #\1) n))) > (set [s ixs] digs) > (copy s)))) Using format. I.e. transform an input string like "a.b~c.d.e" into the format string "a~ab~~c~ad~ae", and then feed it the digit permutations as arguments, which can now be integers: (defun bindots (str) (let* ((n (countql #\. str)) (fs (flow str (regsub "~" "~~") (regsub "." "~a")))) (collect-each ((digs (rperm '(0 1) n))) (fmt fs . digs)))) Doh, why stick to the collect-each copy paste; it's a mapcar job: (defun bindots (str) (let* ((n (countql #\. str)) (fs (flow str (regsub "~" "~~") (regsub "." "~a")))) (mapcar (ap fmt fs) (rperm '(0 1) n)))) Use flow syntax for the whole body: (defun bindots (str) (flow str (regsub "~" "~~") (regsub "." "~a") (mapcar (ap fmt @@1) (rperm '(0 1) (countql #\. str))))) Why @@1? (ap fmt @1) would refer to the ap expression's own parameter 1. @@1 escapes one level out to access the (mapcar ...) expression's implicit parameter 1, which is the prepared format string coming out of the previous regsub. The op syntax, inspired the op operator in MIT Goo, has a fully developed argument referencing system that supports nesting, reminiscent of nested commas in backquotes. Whenver an op expression explicitly refers to parameter material using @1, @2, ... or @rest, this has the effect of suppressing the insertion of the implicit rightmost parameter @1, the idea being that the expression is taking full control over what arguments are inserted where. Thus because @@1 mentions an implicit parameter of the mapcar expression, that expression no longer receives an implicit rightmost argument. We want this, because otherwise the format string object from the previous regsub pipeline element would appear as an extra sequence for mapcar to traverse. Instead of rperm, we could also count up to below (expt 2 n), and then use digits to explode digits, like this. Ah, right but no, because we don't get leading zeros. I will still mention this: 1> (mapcar (lop digits 2) 0..16) ((0) (1) (1 0) (1 1) (1 0 0) (1 0 1) (1 1 0) (1 1 1) (1 0 0 0) (1 0 0 1) (1 0 1 0) (1 0 1 1) (1 1 0 0) (1 1 0 1) (1 1 1 0) (1 1 1 1)) In TXR, I have moved toward generic iteration. Most of the library is converted. Actually since now ancient history, sequences other than list have been traversable with car and cdr, and still are. But that convenience is inefficient due to copying. There is an iteration system which works like this: 1> (iter-begin '(a b c)) (a b c) 2> (iter-more *1) t 3> (iter-item *1) a 4> (iter-step *1) (b c) 5> (iter-begin "abc") # 6> (iter-more *5) t 7> (iter-item *5) #\a 8> (iter-step *5) # 9> (iter-item *5) #\b Iterators can be functional or stateful, so the return value of iter-step must be captured. Above, (iter-begin "abc") produces a heap object, whereas (iter-begin '(a b c)) just returns (a b c). mapcar efficiently allocates the iterator objects on the stack. In that case, list iteration uses an iterator object also; it's not just using the list as an iterator. The public function iter-begin performs this optimization whereby for certain objects, an iteration object need not be constructed, because iter-more, iter-item and iter-step can handle the original representation. E.g. infinitely iterate from 3: 1> (iter-begin 3) ;; identity 3 2> (iter-more *1) ;; unconditional true t 3> (iter-item *1) ;; identity 3 4> (iter-step *1) ;; successor function 4 Since numbers are iterable, we can easily add numbering in a mapcar, just by mentioning a number as one of the sequence arguments. 1> (mapcar (do pic `>>:0#` @1 @2) "AA".."DD" 0) ("AA:00" "AB:01" "AC:02" "AD:03" "BA:04" "BB:05" "BC:06" "BD:07" "CA:08" "CB:09" "CC:10" "CD:11" "DA:12" "DB:13" "DC:14" "DD:15") do is the variant of op which handles macros rather than functions. The pipelining opip (basis of flow macro) automatically applies do or op to the pipeline elements based on whether they have an operator or function binding. Thus we can do (flow whatever (mapcar ...) (if ...) (progn ..)) The mapcar will be treated as an (op mapcar ...) the if and progn as (do if ...) and (do progn ...). -- TXR Programming Language: http://nongnu.org/txr Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal Mastodon: @Kazinator@mstdn.ca