Parsing strings representing lists of integers and integer spans - elisp

I am looking for a function that parses integer lists in Emacs Lisp, along the lines of Perl's Set::IntSpan. I.e., I would like to be able to do something like this:
(parse-integer-list "1-3, 4, 8, 18-21")
⇒ (1 2 3 4 8 18 19 20 21)
Is there an elisp library somewhere for this?

The following does what you want:
(defun parse-integer-list (str)
"Parse string representing a range of integers into a list of integers."
(let (start ranges)
(while (string-match "\\([0-9]+\\)\\(?:-\\([0-9]+\\)\\)?" str start)
(push
(apply 'number-sequence
(seq-map 'string-to-int
(seq-filter
'identity
(list (match-string 1 str) (match-string 2 str)))))
ranges)
(setq start (match-end 0)))
(nreverse (seq-mapcat 'nreverse ranges))))
The code loops over the incoming string searching for plain numbers or ranges of numbers. On each match it calls number-sequence with either just a number for a plain match or two numbers for a range match and pushes each resulting number sequence into a list. To account for push building the result backwards, at the end it reverses all ranges in the list, concatenates them, then reverses the result and returns it.
Calling parse-integer-list with your example input:
(parse-integer-list "1-3, 4, 8, 18-21")
produces:
(1 2 3 4 8 18 19 20 21)

Related

"Group" a list by "keys"?

I want to map a "flat" structure like:
'(("Jimmy Carter" 10 1 1924)
("Donald Trump" 6 14 1946)
("George W. Bush" 7 6 1946)
("Bill Clinton" 8 19 1946)
("Barack Obama" 8 4 1961))
to:
'((1924
(10
(1
("Jimmy Carter"))))
(1946
(6
(14
("Donald Trump")))
(7
(6
("George W. Bush")))
(8
(19
("Bill Clinton"))))
(1961
(8
(4
("Barack Obama")))))
i. e. group a list by some "keys", in this example year, month and day of birth. What is the best way to do that for the general case? Is there another solution for a simple case where there is only one "key" per element (say, 'democrat and 'republican)?
I suggest that you use hash-tables:
(let ((ht (make-hash-table))
(ret ())
(dolist (record my-data)
(push record (gethash (my-key record) ht ())))
(maphash (lambda (key records)
(push (list key (transform record)) ret))
ht)
ret)
here
my-data is your original list
my-key extracts the grouping key (e.g., year)
transform maps the flat list (name a b c) to the hierarchy you want to see in the return value.

Scheme add columns in a matrix

I am trying to write a function that takes a matrix (represented as a list of lists) and adds the elements down the columns and returns a vector (represented as a list):
Example:
(define sample
'((2 6 0 4)
(7 5 1 4)
(6 0 2 2)))
should return '(15 11 3 10).
I was trying to use the (list-ref) function twice to obtain the first element of each column with no luck. I am trying something like:
(map (lambda (matrix) ((list-ref (list-ref matrix 0) 0)) (+ matrix))
The solution is simple if we forget about the indexes and think about higher-order procedures, try this:
(define sample
'((2 6 0 4)
(7 5 1 4)
(6 0 2 2)))
(apply map + sample)
=> '(15 11 3 10)
Explanation: map can take multiple lists as arguments. If we apply it to sample (which is a list of lists) and pass + as the procedure to do the mapping, it'll take one element from each list in turn and add them, producing a list with the results - effectively, adding all the columns in the matrix.

Sum of numbers in a list using Scheme

I want to sum the numbers in a list without using recursion. I know you can sum a list of numbers like this
(+ num1 num2 ... numN)
but what if you have a list L which equals to '(num1 num2 ... numN)
is there a way to make + take the numbers in this list as arguments. I need to do this without recursion or helper functions.
Sure, just use apply:
(apply + '(1 2 3 4 5 6)) ; same as (+ 1 2 3 4 5 6)
(apply + 1 2 3 '(4 5 6)) ; ditto
(apply + 1 2 3 4 5 '(6)) ; ditto
(apply + 1 2 3 4 5 6 '()) ; ditto
The general answer to the question you seem to be asking -- how to take a list and use it as the arguments -- is apply, as Chris Jester-Young answered.
However, for this particular question, there might some other considerations. You may want to sum lists of arbitrary size. However, implementations often have some limit of the number of arguments you can call a function with. A more reliable solution may be to use some kind of fold function (various implementations have different fold functions) to fold + over the list.

Sort two list with the elements in an increasing order

The question requires me to Complete the Scheme function merge, which consumes two lists of sorted numbers (in an increasing order) and produces a list of numbers which consists of all the two consumed lists in sorted order.
For example,
(merge (list 1 4 5 9) (list -1 2 4)) => (list -1 1 2 4 4 5 9)
(merge (list 1 4 5 9) empty) => (list 1 4 5 9)
(merge empty (list 1 4 5 9)) => (list 1 4 5 9)
(merge empty empty) => empty
Thanks for helping out!!
Since this smells like homework, I won't write any code, but I will tell you that what are doing is part of the merge sort algorithm. Remember these two things:
In functional languages like Scheme, you are asking the question what value do I need to produce rather than what do I need to do
In Scheme, you often write more than one procedure to accomplish a single task
If you remember these two things and figure out which part of merge sort you need to implement, it should become fairly easy to figure out.

Scheme Understanding

I am supposed to write a scheme function (digit-count n) that accepts a positive integer n and evaluates to the number of digits of n that are 6, 4, or 9.
I am having trouble understanding what exactly I am supposed to do, I am confused about the "digits of n that are 6, 4 or 9", what does this mean?
This is just an interpretation question, but I would say that you would take the decimal representation of a number, and count the total number of digits that are 6, 4, or 9. For example:
100 --> 0
4 --> 1
469 --> 3
444 --> 3
Get it now?
One interpretation - example:
Given 678799391, the number of digits would be 0 for 4, 1 for 6 and 3 for 9. The sum of the occurences would be 0 + 1 + 3 = 4.
Convert the whole number to a list and check each one individually.
(define (number->list x)
(string->list (number->string x))
(define (6-4-or-9 x) (cond ((= x 6) true)) ((= x 4) true)) ((= x 9) true))))
(define (count-6-4-9 x) (cond ((6-4-or-9 (car (number->list x)))).......
I'm sure you can see where that's going. It's pretty crude, and I'm not sure it's really idiomatic, but it should work.
The idea is is that you convert the number to a list, check to first digit, if it's six, four or nine, recursively call the procedure on the cdr of the number list converted back to a string + 1...
If you are not using lists, you can work with modulo % of 10 and dividing whole numbers / with 10.
Below is the recursive solution :
(define (digits n)
(if(not (< n 1)) (+ 1 (digits (/ n 10))) 0))
First, we must understand what the question is asking: It is asking you to write a procedure that counts the number of times the numbers 4, 6, or 9 show up in another inputted number. For instance, inputting 10345 should return 1. Let's see why:
The digits of 10345 are 1, 0, 3, 4, and 5. We have to ask, "How many times do 4, 6, or 9 show up?" Well, there are no 6's or 9's in 10345. However, there is one 4. Therefore, the procedure should return 1.
Another example: (digit-count 14289)
Let's break it up like we did before. The digits of 14289 are 1, 4, 2, 8, and 9. There are no 6's. There are, however, 1's and 9's. How many? There is one 1 and one 9. Since there are two (total) of the desired digits present (desired digits are 4, 6, and 9), (digit-count 14289) should return 2.
Some more examples:
(digit-count 144) --> 2 (there are two 4's)
(digit-count 1) --> 0 (there are no 4's, 6's, or 9's)
(digit-count 1262) --> 1 (there is one 6)
Now, let's start defining. We can take advantage of the appearances function, which takes two inputs and returns how many times the first input appears in the second.
For example: (appearances 'a 'amsterdam) returns 2 because there are two a's in amsterdam.
Using appearances, here is our definition (finally!):
(define (count469 num)
(+ (appearances 4 num)
(appearances 6 num)
(appearances 9 num)))
This function returns the sum of the appearances of 4, the appearances of 6, and the appearances of 9. Please feel free to reply with any feedback or questions!

Resources