如何在 Racket 中访问子结构变量 (Lisp/Scheme)

How to access sub-struct variables in Racket (Lisp/Scheme)

我正在尝试将 AM/PM 时钟转换函数编写为 24 小时制(军用时间),但是有一个结构和子结构具有我无法弄清楚如何提取的变量. 24HourClock-hours 或 AM/PM-hours 都无法从 AM/PM 时间实例中提取小时或分钟变量,该时间实例作为函数的两个版本中的每一个的参数。

以下两个函数是对同一函数的两次独立尝试:

(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)]))

这个尝试使用 lambda 函数访问子成员:

(define (AM/PM->24HourClock time)
  (lambda (hr min meridiem)
    (cond
      [(isAM? time) (24HourClock (hr min))]
      [else (24HourClock (+ hr 12) min)]))

但这是输出:

这是使其可重现的早期代码:

#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->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)])))

但是由于 AM/PM-hoursAM/PM-minutes 没有像你在这里用 24HourClock 定义为超级 class 那样定义时,你必须写:

(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)])))

你的问题是你认为struct-slot本身就是struct中slot的值。但事实并非如此。它只是 structslot 的 getter 函数。因此,您必须执行 (struct-slot instance) 才能在类型为 struct.

instance 中获取 slot 的值

如果你想要AM/PM->24HourClock,那么time就是AM/PM的类型。

这是所有代码的组合,与您的回复和显示的内容相同:

#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)])))

这是我用于测试用例的内容:

;=======================
;    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"))