'How to access sub-struct variables in Racket (Lisp/Scheme)
I'm trying to write an AM/PM clock conversion function to a 24 hour clock (military time), but there's a struct and sub-struct that have variables I can't figure out how to pull. Neither 24HourClock-hours or AM/PM-hours is working to pull the hours or minutes variable out of the AM/PM instance of time that goes as the parameter of each of the two versions of the function.
The following two functions are two separate attempts at the same function:
(define (AM/PM->24HourClock2 time)
(cond
[(isAM? time) (24HourClock (24HourClock-hours 24HourClock-minutes))]
[else (24HourClock (+ 24HourClock-hours 12) 24HourClock-minutes)]))
(define (AM/PM->24HourClock time)
(cond
[(isAM? time) (24HourClock (AM/PM-hours AM/PM-minutes))]
[else (24HourClock (+ AM/PM-hours 12) AM/PM-minutes)]))
This one attempts to use a lambda function to access the sub-members:
(define (AM/PM->24HourClock time)
(lambda (hr min meridiem)
(cond
[(isAM? time) (24HourClock (hr min))]
[else (24HourClock (+ hr 12) min)]))
But here's the output:
Here's the earlier code that makes it reproducible:
#lang slideshow
(struct 24HourClock (hours minutes)
#:transparent
#:mutable
#:guard (lambda (hours minutes err_name)
(if (not (and (>= hours 0) (< hours 24)))
(error err_name "Hours must be between 0 and 24")
(if (not (and (>= minutes 0) (< minutes 60)))
(error err_name "Minutes must be between 0 and 60")
(values hours minutes)))))
(struct AM/PM 24HourClock (meridiem)
#:transparent
#:mutable
#:guard (lambda (hours minutes meridiem err_name)
(if (not(and(and(number? hours)(> hours 0)(<= hours 12)(number? minutes))))
(error err_name "Hours must be between 1 and 12 : 0")
(if [not (and(string? meridiem)(or
(equal? meridiem "am")
(equal? meridiem "AM")
(equal? meridiem "pm")
(equal? meridiem "PM")))]
(error err_name "Invalid value for meridiem")
(values hours minutes meridiem)))))
(define (isAM? time)
(or (equal? (AM/PM-meridiem time) "AM")
(equal? (AM/PM-meridiem time) "am")))
Solution 1:[1]
It should be rather
(define (AM/PM->24HourClock2 time)
(cond
[(isAM? time) (24HourClock (AM/PM-hours time) (AM/PM-minutes time))]
[else (24HourClock (+ (AM/PM-hours time) 12) (AM/PM-minutes time))]))
(define (AM/PM->24HourClock time)
(let ((hr (AM/PM-hours time))
(min (AM/PM-minutes time))
(mer (AM/PM-meridiem time)))
(cond
[(isAM? time) (24HourClock hr min)]
[else (24HourClock (+ hr 12) min)])))
But since AM/PM-hours and AM/PM-minutes are not defined when defining like you did here with 24HourClock as super class, you have to write:
(define (AM/PM->24HourClock2 time)
(cond
[(isAM? time) (24HourClock (24HourClock-hours time) (24HourClock-minutes time))]
[else (24HourClock (+ (24HourClock-hours time) 12) (24HourClock-minutes time))]))
(define (AM/PM->24HourClock time)
(let ((hr (24HourClock-hours time))
(min (24HourClock-minutes time))
(mer (AM/PM-meridiem time)))
(cond
[(isAM? time) (24HourClock hr min)]
[else (24HourClock (+ hr 12) min)])))
Your problem is that you think struct-slot is itself the value of slot in struct. But it isn't. It is just the getter function for the slot in struct. Therefore, you have to do (struct-slot instance) to get the value of slot in the instance which is of the type struct.
If you want AM/PM->24HourClock, then time is of the type AM/PM.
Solution 2:[2]
This is all code put together, it's the same as your response and what was shown:
#lang slideshow
(struct 24HourClock (hours minutes) #:transparent #:mutable
#:guard (lambda (hours minutes err_name)
(if (not (and (>= hours 0) (< hours 24)))
(error err_name "Hours must be between 0 and 24")
(if (not (and (>= minutes 0) (< minutes 60)))
(error err_name "Minutes must be between 0 and 60")
(values hours minutes)))))
(struct AM/PM 24HourClock (meridiem) #:transparent #:mutable
#:guard (lambda (hours minutes meridiem err_name)
(if (not(and(and(number? hours)(> hours 0)(<= hours 12)(number? minutes))))
(error err_name "Hours must be between 1 and 12 : 0")
(if [not (and(string? meridiem)(or
(equal? meridiem "am")
(equal? meridiem "AM")
(equal? meridiem "pm")
(equal? meridiem "PM")))]
(error err_name "Invalid value for meridiem")
(values hours minutes meridiem)))))
(define (isAM? time)
(or (equal? (AM/PM-meridiem time) "AM")
(equal? (AM/PM-meridiem time) "am")))
(define (AM/PM->24HourClock time)
(let ((hr (AM/PM-hours time))
(min (AM/PM-minutes time))
(cond
[(isAM? time) (24HourClock hr min)]
[else (24HourClock (+ hr 12) min)])))
Here's what I use for test cases:
;=======================
; Test Cases
;=======================
; (24HourClock 0 20)
; (24HourClock 24 20)
; (24HourClock 11 60)
; (AM/PM 0 20 "AM")
; (AM/PM 1 20 "Am")
; (AM/PM 2 20 "am")
; (is-AM? 2 20 "am")
; (AM/PM->24HourClock (AM/PM 0 10 "AM"))
; (AM/PM->24HourClock (AM/PM 12 10 "am"))
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | cocoakrispies93 |


