SML programming to racket programming - racket

Using SML, I have completed a problem called dates_in_month that takes a list of dates and a month (i.e., an int) and returns a list holding the dates from the argument list of dates that are in the month. While it's done and easy in SML, my question is how this would I write the same program in the Racket programming language, where functions and lists are defined and used differently?
An example is (dates_in_month 5 [(2002 05 12) (1999 05 12) (1980 12 20)]), and the output should be (2002 05 12) (1999 05 12) because those are the dates where the months is 5.

This is relatively straightforward with filter:
(define (dates_in_month n l)
(filter (lambda (date) (= (cadr date) n)) l))
then
> (dates_in_month 5 '[(2002 5 12) (1999 5 12) (1980 12 20)])
'((2002 5 12) (1999 5 12))
>

Related

Application not a procedure error in Racket

I'm trying to write a function called dates_in_month that takes a list of dates and a month and returns a list holding the dates from the argument list of dates that are in the month. The returned list should contain dates in the order they were originally given. However I'm new to Racket and I'm getting the error "application: not a procedure;
expected a procedure that can be applied to arguments
given: 5"
Does anyone know what this means or how to fix it? If anyone can point out my error that'd be much appreciated.
This is the code i am working on with my test case at the bottom.
#lang racket
(define (append lst1 lst2)
(if (null? lst1)
lst2
(cons (car lst1) (append (cdr lst1) lst2))))
(define (dates_in_month dates month)
(if (null? dates)
'()
(let ((date (car dates)))
(if (= (month date) month)
(cons date (dates_in_month (cdr dates) month))
(dates_in_month (cdr dates) month)))))
(define test-dates '(#(1 1 2000) #(2 2 2000) #(3 3 2000) #(4 4
2000) #(5 5 2000) #(6 6 2000)))
(dates_in_month test-dates 5)
Your error is caused by calling (month date)- month should be some procedure, which you want to call with the argument date (date will be some vector), but month has value 5, that isn't a procedure.
That is the meaning of the error message:
"application: not a procedure; expected a procedure that can be applied to arguments given: 5"
I guess you need to get somehow the second element of the vector date and then compare it with the value of month. You should use the function vector-ref- example:
> (vector-ref #(5 5 2000) 1)
5
See also DrRacket docs for Vectors for other functions for working with vectors.
And if you can, you could also use filter instead of recursion. Here are both variants:
(define (dates-in-month dates month)
(if (null? dates)
'()
(let ((date (car dates)))
(if (= (vector-ref date 1) month)
(cons date (dates-in-month (cdr dates) month))
(dates-in-month (cdr dates) month)))))
(define (dates-in-month2 dates month)
(filter (lambda (date) (= (vector-ref date 1) month))
dates))
(define test-dates '(#(1 1 2000) #(2 2 2000) #(3 3 2000) #(4 4 2000) #(5 5 2000) #(6 6 2000)))
(dates-in-month test-dates 5)
(dates-in-month2 test-dates 5)
And append is already part of the DrRacket language, so you don't have to reimplement it.

Iteration over a list with a for loop (Racket)

I need help with writing a function that uses a for loop to iterate over a list of numbers and returns all numbers that are less than 10. I have no idea where to start if anyone could help with even a starting point I'd appreciate it.
Thank you :)
The "standard" way to do such a thing is with the filter procedure. It takes a predicate (a procedure that indicates whether a condition is true) and a list, and produces a new list containing those elements in the input list that satisfy the predicate. For example, we can produce a list that contains only the even numbers from an input list of numbers like so:
(filter even? '(0 1 2 3 4 5 6))
;; => (0 2 4 6)
In your case, there's no "built in" predicate for testing whether a number is less than 10, but we can easily define one:
(define (<10 n)
(< n 10))
and then filter any list using it:
(filter <10 '(1 4 15 23 25 4 8))
;; => (1 4 4 8)
In this case though, I think most would prefer to use a lambda instead:
(filter (lambda (n) (< n 10))
'(1 4 15 23 25 4 8))
;; => (1 4 4 8)
Hope this helps!

How to list all the leap year from 1800 in LISP?

I have this code below which takes one parameter and prints all the list of leap year in reverse order. how can I make it take 1800 as default input and just run command (leap) to list all the leap years from 1800-2018?
CODE:
(defun leap (q)
(if (< q 1800)
(RETURN-FROM leap nil)
)
(leap (- q 1))
(if (leapyear q)
(push q mylist)
)
mylist
)
(reverse(leap 2018))
I can't completely understand what you are trying to do, but:
(defun leapyearp (y)
;; is Y a leap year, as best we can tell?
(= (nth-value 3 (decode-universal-time
(+ (encode-universal-time 0 0 0 28 2 y)
(* 60 60 24))))
29))
(defun leapyears (&key (start 1800) (end (nth-value 5 (get-decoded-time))))
;; all the leap years in a range
(loop for y from start to end
if (leapyearp y) collect y))

Little lisp challenge [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I have a string like "2d 6:36", and I want to multiply that amount of hours by a constant.
This means I want this to be calculated:
(2*24 + 6 + 36/60) * constant
or, in general
(d*24 + h + m/60)
Any help?
Edit: I only have the string not, the d, h and m.
You need parse your string in something useful, for example a list of integers, you can use cl-ppcre for this:
(defun parse-day-and-time (string)
"Get string like 3d 23:40 and return list (3 23 40)"
(multiple-value-bind (str matches)
(cl-ppcre:scan-to-strings "(\\d+)d ([0-9]|0[0-9]|1[0-9]|2[0-3]):([0-5][0-9])" string)
(declare (ignore str))
(when (< (length matches) 3)
(error "Wrong day and time string: ~A" string))
(map 'list #'parse-integer matches)))
Then you can do yout calculation:
(defun mult-time (string-time coeff)
(destructuring-bind (days hours minutes)
(parse-day-and-time string-time)
(* coeff (+ (* 24 days) hours (/ minutes 60)))))
CL-USER> (mult-time "2d 6:36" 300)
16380
Of cause if you want to do some more calculations with your time, may be it will be better to represent it as amount of seconds, do your calculation with it, then return it in any string you want.
So I understand the data to be a human readable amount of time.. E.g. "2d 1:35" represent 2 days, 1 hour and 35 seconds or 595/12 hours. I'm tempted to let loose the spec for the string like this:
(defparameter *units* '(("w" . 168) ("d" . 24) (":" . 1)
("h" . 1) ("m" . 1/60) ("s" . 1/3600)))
(defun str-to-hours (str &optional (acc 0))
(or
(cl-ppcre:register-groups-bind (num unit rest)
("(\\d+)\\s*(\\D{0,1})\\D*(.*)" str :sharedp t)
(str-to-hours rest
(+ acc
(* (parse-integer num)
(or (cdr (assoc (string-downcase unit)
*units*
:test #'equal))
1/60)))))
acc))
(str-to-hours "2d 6:36") ; ==> 273/5 ~54.6
(str-to-hours "2D6H36M") ; ==> 273/5 ~54.6
(str-to-hours "2 weeks, 1 day, 3 hours, 7 minutes and 10 seconds") ; ==> 130723/360 ~363.11945
(* 10 (str-to-hours "2d6:36")) ; ==> 546

How to count days excluding weekends and holidays in Emacs calendar

In Emacs calendar, one can count days between two dates (including both the start and the end date) using the M-= which runs the command calendar-count-days-region. How can I count days excluding the weekends (Saturday and Sunday) and if defined holidays coming from the variables: holiday-general-holidays and holiday-local-holidays?
I think this essentially breaks down into three parts:
Count the days in a region
subtract the weekend days
subtract the holidays
Emacs already has the first part covered with M-= (calendar-count-days-region), so let's take a look at that function.
Helpful, but unfortunately it reads the buffer and sends the output directly. Let's make a generalized version which takes start and end date parameters and returns the number of days instead of printing them:
(defun my-calendar-count-days(d1 d2)
(let* ((days (- (calendar-absolute-from-gregorian d1)
(calendar-absolute-from-gregorian d2)))
(days (1+ (if (> days 0) days (- days)))))
days))
This is pretty much just a copy of the calendar-count-days-region function, but without the buffer reading & writing stuff. Some tests:
(ert-deftest test-count-days ()
"Test my-calendar-count-days function"
(should (equal (my-calendar-count-days '(5 1 2014) '(5 31 2014)) 31))
(should (equal (my-calendar-count-days '(12 29 2013) '(1 4 2014)) 7))
(should (equal (my-calendar-count-days '(2 28 2012) '(3 1 2012)) 3))
(should (equal (my-calendar-count-days '(2 28 2014) '(3 1 2014)) 2)))
Now, for step 2, I can't find any built-in function to calculate weekend days for a date range (surprisingly!). Luckily, this /might/ be pretty simple when working with absolute dates. Here's a very naive attempt which simply loops through all absolute dates in the range and looks for Saturdays & Sundays:
(defun my-calendar-count-weekend-days(date1 date2)
(let* ((tmp-date (if (< date1 date2) date1 date2))
(end-date (if (> date1 date2) date1 date2))
(weekend-days 0))
(while (<= tmp-date end-date)
(let ((day-of-week (calendar-day-of-week
(calendar-gregorian-from-absolute tmp-date))))
(if (or (= day-of-week 0)
(= day-of-week 6))
(incf weekend-days ))
(incf tmp-date)))
weekend-days))
That function should be optimized since it does a bunch of unnecessary looping (e.g. we know that the 5 days after Sunday won't be weekend days, so there is no need to convert & test them), but for the purpose of this example I think it's pretty clear and simple. Good Enough for now, indeed. Some tests:
(ert-deftest test-count-weekend-days ()
"Test my-calendar-count-weekend-days function"
(should (equal (my-calendar-count-weekend-days
(calendar-absolute-from-gregorian '(5 1 2014))
(calendar-absolute-from-gregorian '(5 31 2014))) 9))
(should (equal (my-calendar-count-weekend-days
(calendar-absolute-from-gregorian '(4 28 2014))
(calendar-absolute-from-gregorian '(5 2 2014))) 0))
(should (equal (my-calendar-count-weekend-days
(calendar-absolute-from-gregorian '(2 27 2004))
(calendar-absolute-from-gregorian '(2 29 2004))) 2)))
Lastly, we need to know the holidays in the range, and emacs provides this in the holiday-in-range function! Note that this function calls calendar-holiday-list to determine which holidays to include, so if you really want to search only holiday-general-holidays and holiday-local-holidays you would need to set your calendar-holidays variable appropriately. See C-h v calendar-holidays for the details.
Now we can wrap all this up in a new interactive function which does the three steps above. This is essentially another modified version of calendar-count-days-region that subtracts weekends and holidays before printing the results (see edit below before running):
(defun calendar-count-days-region2 ()
"Count the number of days (inclusive) between point and the mark
excluding weekends and holidays."
(interactive)
(let* ((d1 (calendar-cursor-to-date t))
(d2 (car calendar-mark-ring))
(date1 (calendar-absolute-from-gregorian d1))
(date2 (calendar-absolute-from-gregorian d2))
(start-date (if (< date1 date2) date1 date2))
(end-date (if (> date1 date2) date1 date2))
(days (- (my-calendar-count-days d1 d2)
(+ (my-calendar-count-weekend-days start-date end-date)
(my-calendar-count-holidays-on-weekdays-in-range
start-date end-date)))))
(message "Region has %d workday%s (inclusive)"
days (if (> days 1) "s" ""))))
I'm sure someone more knowledgeable about lisp/elisp could simplify/improve these examples considerably, but I hope it at least serves as a starting point.
Actually, now that I've gone through it, I expect somebody to come along any minute and point out that there is an emacs package that already does this...
Edit: DOH!, Bug #001: If a holiday falls on a weekend, that day is removed twice...
Once solution would be to simply wrap holiday-in-range so we can eliminate holidays which were already removed for being on a weekend:
(defun my-calendar-count-holidays-on-weekdays-in-range (start end)
(let ((holidays (holiday-in-range start end))
(counter 0))
(dolist (element holidays)
(let ((day (calendar-day-of-week (car element))))
(if (and (> day 0)
(< day 6))
(incf counter))))
counter))
I've updated the calendar-count-days-region2 above to use this new function.