diff --git a/bench/rich-list-bench-new.scm b/bench/rich-list-bench-new.scm new file mode 100644 index 0000000000000000000000000000000000000000..040c1cffa3efd0a1baa005632322dbf9781b9102 --- /dev/null +++ b/bench/rich-list-bench-new.scm @@ -0,0 +1,142 @@ +; +; Copyright (C) 2025 The Goldfish Scheme Authors +; +; Licensed under the Apache License, Version 2.0 (the "License"); +; you may not use this file except in compliance with the License. +; You may obtain a copy of the License at +; +; http://www.apache.org/licenses/LICENSE-2.0 +; +; Unless required by applicable law or agreed to in writing, software +; distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +; WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +; License for the specific language governing permissions and limitations +; under the License. +; + +;;; New Rich-List Implementation Benchmark +;;; Tests performance of define-case-class-2 based rich-list + +(import (scheme time) + (liii lang_2)) + +(define iterations 1000) +(define test-rounds 5) + +(define (benchmark thunk) + (let* ((start (current-jiffy)) + (result (thunk)) + (end (current-jiffy))) + (- end start))) + +(define (run-multiple times proc) + (when (> times 0) + (proc) + (run-multiple (- times 1) proc))) + +(define (test-and-print name test-proc) + (display* "Testing " name "...\n") + (let ((times '())) + (let loop ((round 1)) + (when (<= round test-rounds) + (display* " Round " round "...") + (let ((time (benchmark test-proc))) + (set! times (cons time times))) + (display "done\n") + (loop (+ round 1)))) + (let ((avg (exact->inexact (/ (apply + times) (length times))))) + (display* " Average: " avg "ns\n") + avg))) + +(display "=== New Rich-List Implementation Benchmark ===\n\n") + +;; Test creation from list +(define new-creation-avg + (test-and-print "rich-list creation" + (lambda () (run-multiple iterations (lambda () (rich-list '(1 2 3 4 5))))))) + +;; Test static range +(define new-range-avg + (test-and-print "range creation" + (lambda () (run-multiple iterations (lambda () (rich-lists :range 1 100)))))) + +;; Test map +(define new-map-avg + (test-and-print "map operation" + (lambda () + (let ((lst (rich-lists :range 1 100))) + (run-multiple iterations (lambda () (lst :map (lambda (x) (* x 2))))))))) + +;; Test filter +(define new-filter-avg + (test-and-print "filter operation" + (lambda () + (let ((lst (rich-lists :range 1 100))) + (run-multiple iterations (lambda () (lst :filter (lambda (x) (> x 50))))))))) + +;; Test take +(define new-take-avg + (test-and-print "take operation" + (lambda () + (let ((lst (rich-lists :range 1 100))) + (run-multiple iterations (lambda () (lst :take 10))))))) + +;; Test head access +(define new-head-avg + (test-and-print "head operation" + (lambda () + (let ((lst (rich-lists :range 1 100))) + (run-multiple iterations (lambda () (lst :apply :head))))))) + +;; Test length +(define new-length-avg + (test-and-print "length operation" + (lambda () + (let ((lst (rich-lists :range 1 100))) + (run-multiple iterations (lambda () (lst :apply :length))))))) + +;; Test contains +(define new-contains-avg + (test-and-print "contains operation" + (lambda () + (let ((lst (rich-lists :range 1 100))) + (run-multiple iterations (lambda () (lst :contains 50))))))) + +;; Test collect +(define new-collect-avg + (test-and-print "collect operation" + (lambda () + (let ((lst (rich-lists :range 1 100))) + (run-multiple iterations (lambda () (lst :apply :collect))))))) + +;; Test chaining +(define new-chain-avg + (test-and-print "chaining operation" + (lambda () + (let ((lst (rich-lists :range 1 100))) + (run-multiple iterations + (lambda () + (lst :map (lambda (x) (* x 2)) + :filter (lambda (x) (> x 50)) + :take 10 + :apply :collect))))))) + +;; Test fold +(define new-fold-avg + (test-and-print "fold operation" + (lambda () + (let ((lst (rich-lists :range 1 100))) + (run-multiple iterations (lambda () (lst :fold 0 +))))))) + +(display "\n=== New Implementation Results ===\n") +(display* "Creation: " new-creation-avg "ns\n") +(display* "Range: " new-range-avg "ns\n") +(display* "Map: " new-map-avg "ns\n") +(display* "Filter: " new-filter-avg "ns\n") +(display* "Take: " new-take-avg "ns\n") +(display* "Head: " new-head-avg "ns\n") +(display* "Length: " new-length-avg "ns\n") +(display* "Contains: " new-contains-avg "ns\n") +(display* "Collect: " new-collect-avg "ns\n") +(display* "Chaining: " new-chain-avg "ns\n") +(display* "Fold: " new-fold-avg "ns\n") \ No newline at end of file diff --git a/bench/rich-list-bench-old.scm b/bench/rich-list-bench-old.scm new file mode 100644 index 0000000000000000000000000000000000000000..99774cf5e820fee399af540565276690adfc1749 --- /dev/null +++ b/bench/rich-list-bench-old.scm @@ -0,0 +1,142 @@ +; +; Copyright (C) 2025 The Goldfish Scheme Authors +; +; Licensed under the Apache License, Version 2.0 (the "License"); +; you may not use this file except in compliance with the License. +; You may obtain a copy of the License at +; +; http://www.apache.org/licenses/LICENSE-2.0 +; +; Unless required by applicable law or agreed to in writing, software +; distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +; WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +; License for the specific language governing permissions and limitations +; under the License. +; + +;;; Old Rich-List Implementation Benchmark +;;; Tests performance of define-case-class based rich-list + +(import (scheme time) + (liii lang)) + +(define iterations 1000) +(define test-rounds 5) + +(define (benchmark thunk) + (let* ((start (current-jiffy)) + (result (thunk)) + (end (current-jiffy))) + (- end start))) + +(define (run-multiple times proc) + (when (> times 0) + (proc) + (run-multiple (- times 1) proc))) + +(define (test-and-print name test-proc) + (display* "Testing " name "...\n") + (let ((times '())) + (let loop ((round 1)) + (when (<= round test-rounds) + (display* " Round " round "...") + (let ((time (benchmark test-proc))) + (set! times (cons time times))) + (display "done\n") + (loop (+ round 1)))) + (let ((avg (exact->inexact (/ (apply + times) (length times))))) + (display* " Average: " avg "ns\n") + avg))) + +(display "=== Old Rich-List Implementation Benchmark ===\n\n") + +;; Test creation from list +(define old-creation-avg + (test-and-print "rich-list creation" + (lambda () (run-multiple iterations (lambda () (rich-list '(1 2 3 4 5))))))) + +;; Test static range +(define old-range-avg + (test-and-print "range creation" + (lambda () (run-multiple iterations (lambda () (rich-list :range 1 100)))))) + +;; Test map +(define old-map-avg + (test-and-print "map operation" + (lambda () + (let ((lst (rich-list :range 1 100))) + (run-multiple iterations (lambda () (lst :map (lambda (x) (* x 2))))))))) + +;; Test filter +(define old-filter-avg + (test-and-print "filter operation" + (lambda () + (let ((lst (rich-list :range 1 100))) + (run-multiple iterations (lambda () (lst :filter (lambda (x) (> x 50))))))))) + +;; Test take +(define old-take-avg + (test-and-print "take operation" + (lambda () + (let ((lst (rich-list :range 1 100))) + (run-multiple iterations (lambda () (lst :take 10))))))) + +;; Test head access +(define old-head-avg + (test-and-print "head operation" + (lambda () + (let ((lst (rich-list :range 1 100))) + (run-multiple iterations (lambda () (lst :head))))))) + +;; Test length +(define old-length-avg + (test-and-print "length operation" + (lambda () + (let ((lst (rich-list :range 1 100))) + (run-multiple iterations (lambda () (lst :length))))))) + +;; Test contains +(define old-contains-avg + (test-and-print "contains operation" + (lambda () + (let ((lst (rich-list :range 1 100))) + (run-multiple iterations (lambda () (lst :contains 50))))))) + +;; Test collect +(define old-collect-avg + (test-and-print "collect operation" + (lambda () + (let ((lst (rich-list :range 1 100))) + (run-multiple iterations (lambda () (lst :collect))))))) + +;; Test chaining +(define old-chain-avg + (test-and-print "chaining operation" + (lambda () + (let ((lst (rich-list :range 1 100))) + (run-multiple iterations + (lambda () + ((((lst :map (lambda (x) (* x 2))) + :filter (lambda (x) (> x 50))) + :take 10) + :collect))))))) + +;; Test fold +(define old-fold-avg + (test-and-print "fold operation" + (lambda () + (let ((lst (rich-list :range 1 100))) + (run-multiple iterations (lambda () (lst :fold 0 +))))))) + +(display "\n=== Old Implementation Results ===\n") +(display* "Creation: " old-creation-avg "ns\n") +(display* "Range: " old-range-avg "ns\n") +(display* "Map: " old-map-avg "ns\n") +(display* "Filter: " old-filter-avg "ns\n") +(display* "Take: " old-take-avg "ns\n") +(display* "Head: " old-head-avg "ns\n") +(display* "Length: " old-length-avg "ns\n") +(display* "Contains: " old-contains-avg "ns\n") +(display* "Collect: " old-collect-avg "ns\n") +(display* "Chaining: " old-chain-avg "ns\n") +(display* "Fold: " old-fold-avg "ns\n") \ No newline at end of file diff --git a/goldfish/liii/lang_2.scm b/goldfish/liii/lang_2.scm new file mode 100644 index 0000000000000000000000000000000000000000..70e694e14927fc652bb90644677599566e160d64 --- /dev/null +++ b/goldfish/liii/lang_2.scm @@ -0,0 +1,617 @@ +; +; Copyright (C) 2024 The Goldfish Scheme Authors +; +; Licensed under the Apache License, Version 2.0 (the "License"); +; you may not use this file except in compliance with the License. +; You may obtain a copy of the License at +; +; http://www.apache.org/licenses/LICENSE-2.0 +; +; Unless required by applicable law or agreed to in writing, software +; distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +; WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +; License for the specific language governing permissions and limitations +; under the License. +; + +(define-library (liii lang_2) + +(import (only (liii base) + u8-string-length any? receive u8-substring) + (only (liii oop_2) + define-case-class-2 display* @ typed-define case-class? + define-object chain-apply object->string) + (only (liii string) + string-join string-null? string-starts? string-contains string-trim + string-trim-right string-trim-both string-remove-prefix string-remove-suffix string-pad + string-pad-right) + (only (liii vector) + vector= vector-every vector-any vector-filter reverse-list->vector + vector-index vector-index-right vector-fold vector-fold-right) + (only (liii sort) list-stable-sort vector-stable-sort) + (only (liii list) + length=? iota take filter count + drop every any take-right drop-right + fold fold-right reduce take-while drop-while list-index) + (only (liii hash-table) + hash-table-update!/default hash-table-for-each hash-table-ref/default hash-table-contains? hash-table-delete! + hash-table-count) + (only (liii bitwise) bitwise-and bitwise-ior arithmetic-shift) + (liii error)) + +(export + @ typed-define define-case-class-2 define-object + case-class? class=? display* object->string + rich-list rich-lists + option none + box $ +) + +(begin + +(define (box x) + (cond ((integer? x) (rich-integer x)) + ((rational? x) (rich-rational x)) + ((float? x) (rich-float x)) + ((char? x) (rich-char x)) + ((string? x) (rich-string x)) + ((list? x) (rich-list x)) + ((vector? x) (rich-vector x)) + ((hash-table? x) (rich-hash-table x)) + (else (type-error "box: x must be integer?, rational?, float?, char?, string?, list?, vector?, hash-table?")))) + +(define ($ x . xs) + (if (null? xs) (box x) (apply (box x) xs))) + +(define (class=? left right) + (cond + ((and (case-class? left) (case-class? right)) + (left :equals right)) + ((case-class? left) + (left :equals ($ right))) + ((case-class? right) + ($ left :equals right)) + (else + (equal? left right)))) + + +;;; ======================================== +;;; option - Instance methods +;;; ======================================== + +(define-case-class-2 option ((value any?)) + +(define (%get) + (if (null? value) + (value-error "option is empty, cannot get value") + value)) + +(define (%get-or-else default) + (cond ((not (null? value)) value) + ((and (procedure? default) (not (let? default))) + (default)) + (else default))) + +(define (%or-else default . args) + (when (not (and (let? default) (default 'value))) + (type-error "The first parameter of option%or-else must be an option")) + + ; Apply chain operations if provided + (let ((result (if (null? value) + default + (option value)))) + (if (null? args) + result + (apply (result :apply) args)))) + +(define (%equals that) + (and (let? that) + (defined? 'value that) + (equal? value (that 'value)))) + +(define (%defined?) + (not (null? value))) + +(define (%empty?) + (null? value)) + +(define (%forall f) + (if (null? value) + #f + (f value))) + +(define (%exists f) + (if (null? value) + #f + (f value))) + +(define (%contains elem) + (if (null? value) + #f + (equal? value elem))) + +(define (%for-each f) + (when (not (null? value)) + (f value))) + +(define (%map f . args) + (let ((result (if (null? value) + (option '()) + (option (f value))))) + (if (null? args) + result + (apply (result :apply) args)))) + +(define (%flat-map f . args) + (let ((result (if (null? value) + (option '()) + (f value)))) + (if (null? args) + result + (apply (result :apply) args)))) + +(define (%filter pred . args) + (let ((result (if (or (null? value) (not (pred value))) + (option '()) + (option value)))) + (if (null? args) + result + (apply (result :apply) args)))) + +(define (%apply msg . args) + (if (defined? msg %this #t) + (apply (%this msg) args) + (error 'undefined-method (format #f "Method ~a not found" msg)))) + +) ; end of option + +;;; ======================================== +;;; Static methods and constructors +;;; ======================================== + +(define (none) + (option '())) + + +;;; ======================================== +;;; rich-list - Instance methods +;;; ======================================== + +(define-case-class-2 rich-list ((data list?)) + +(define (%collect) data) + +(define (%find pred) + (let loop ((lst data)) + (cond + ((null? lst) (none)) + ((pred (car lst)) (option (car lst))) + (else (loop (cdr lst)))))) + +(define (%find-last pred) + (let ((reversed-list (reverse data))) ; 先反转列表 + (let loop ((lst reversed-list)) + (cond + ((null? lst) (none)) ; 遍历完未找到 + ((pred (car lst)) (option (car lst))) ; 找到第一个匹配项(即原列表最后一个) + (else (loop (cdr lst))))))) ; 继续查找 + +(define (%head) + (if (null? data) + (error 'out-of-range "rich-list%head: list is empty") + (car data))) + +(define (%head-option) + (if (null? data) + (none) + (option (car data)))) + + +(define (%last) + (if (null? data) + (index-error "rich-list%last: empty list") + (car (reverse data)))) + +(define (%last-option) + (if (null? data) + (none) + (option (car (reverse data))))) + +(define (%slice from until . args) + (chain-apply args + (let* ((len (length data)) + (start (max 0 (min from len))) + (end (max 0 (min until len)))) + (if (< start end) + (rich-list (take (drop data start) (- end start))) + (rich-list '()))))) + +(define (%empty?) + (null? data)) + +(define (%equals that) + (let* ((l1 data) + (l2 (that 'data)) + (len1 (length l1)) + (len2 (length l2))) + (if (not (eq? len1 len2)) + #f + (let loop ((left l1) (right l2)) + (cond ((null? left) #t) + ((not (class=? (car left) (car right))) #f) + (else (loop (cdr left) (cdr right)))))))) + +(define (%forall pred) + (every pred data)) + +(define (%exists pred) + (any pred data)) + +(define (%contains elem) + (%exists (lambda (x) (equal? x elem)))) + +(define (%map x . args) + (chain-apply args + (rich-list (map x data)))) + +(define (%flat-map x . args) + (chain-apply args + (rich-list (flat-map x data)))) + +(define (%filter x . args) + (chain-apply args + (rich-list (filter x data)))) + +(define (%for-each x) + (for-each x data)) + +(define (%reverse . args) + (chain-apply args + (rich-list (reverse data)))) + +(define (%take x . args) + (chain-apply args + (begin + (define (scala-take data n) + (unless (list? data) + (type-error + (format #f "In funtion #<~a ~a>: argument *~a* must be *~a*! **Got ~a**" + scala-take '(data n) 'data "list" (object->string data)))) + (unless (integer? n) + (type-error + (format #f "In funtion #<~a ~a>: argument *~a* must be *~a*! **Got ~a**" + scala-take '(data n) 'n "integer" (object->string n)))) + + (cond ((< n 0) '()) + ((>= n (length data)) data) + (else (take data n)))) + + (rich-list (scala-take data x))))) + +(define (%drop x . args) + (chain-apply args + (begin + (define (scala-drop data n) + (unless (list? data) + (type-error + (format #f "In funtion #<~a ~a>: argument *~a* must be *~a*! **Got ~a**" + scala-drop '(data n) 'data "list" (object->string data)))) + (unless (integer? n) + (type-error + (format #f "In funtion #<~a ~a>: argument *~a* must be *~a*! **Got ~a**" + scala-drop '(data n) 'n "integer" (object->string n)))) + + (cond ((< n 0) data) + ((>= n (length data)) '()) + (else (drop data n)))) + + (rich-list (scala-drop data x))))) + +(define (%take-right x . args) + (chain-apply args + (begin + (define (scala-take-right data n) + (unless (list? data) + (type-error + (format #f "In funtion #<~a ~a>: argument *~a* must be *~a*! **Got ~a**" + scala-take-right '(data n) 'data "list" (object->string data)))) + (unless (integer? n) + (type-error + (format #f "In funtion #<~a ~a>: argument *~a* must be *~a*! **Got ~a**" + scala-take-right '(data n) 'n "integer" (object->string n)))) + + (cond ((< n 0) '()) + ((>= n (length data)) data) + (else (take-right data n)))) + + (rich-list (scala-take-right data x))))) + +(define (%drop-right x . args) + (chain-apply args + (begin + (define (scala-drop-right data n) + (unless (list? data) + (type-error + (format #f "In funtion #<~a ~a>: argument *~a* must be *~a*! **Got ~a**" + scala-drop-right '(data n) 'data "list" (object->string data)))) + (unless (integer? n) + (type-error + (format #f "In funtion #<~a ~a>: argument *~a* must be *~a*! **Got ~a**" + scala-drop-right '(data n) 'n "integer" (object->string n)))) + + (cond ((< n 0) data) + ((>= n (length data)) '()) + (else (drop-right data n)))) + + (rich-list (scala-drop-right data x))))) + +(define (%count . xs) + (cond ((null? xs) (length data)) + ((length=? 1 xs) (count (car xs) data)) + (else (error 'wrong-number-of-args "rich-list%count" xs)))) + +(define (%length) + (length data)) + +(define (%fold initial f) + (fold f initial data)) + +(define (%fold-right initial f) + (fold-right f initial data)) + +(define (%sort-with less-p . args) + (chain-apply args + (let ((sorted-data (list-stable-sort less-p data))) + (rich-list sorted-data)))) + +(define (%sort-by f . args) + (chain-apply args + (let ((sorted-data (list-stable-sort (lambda (x y) (< (f x) (f y))) data))) + (rich-list sorted-data)))) + +(define (%group-by func) + (let ((group (make-hash-table))) + (for-each + (lambda (elem) + (let ((key (func elem))) + (hash-table-update!/default + group + key + (lambda (current-list) (cons elem current-list)) + '()))) + data) + (hash-table-for-each + (lambda (k v) (hash-table-set! group k (reverse v))) + group) + (rich-hash-table group))) + +(define (%sliding size . step-arg) + (unless (integer? size) (type-error "rich-list%sliding: size must be an integer " size)) + (unless (> size 0) (value-error "rich-list%sliding: size must be a positive integer " size)) + + (let ((N (length data))) + (if (null? data) + #() + (let* ((is-single-arg-case (null? step-arg)) + (step (if is-single-arg-case 1 (car step-arg)))) + + (when (and (not is-single-arg-case) + (or (not (integer? step)) (<= step 0))) + (if (not (integer? step)) + (type-error "rich-list%sliding: step must be an integer " step) + (value-error "rich-list%sliding: step must be a positive integer " step))) + + (if (and is-single-arg-case (< N size)) + (vector data) + (let collect-windows ((current-list-segment data) (result-windows '())) + (cond + ((null? current-list-segment) (list->vector (reverse result-windows))) + ((and is-single-arg-case (< (length current-list-segment) size)) + (list->vector (reverse result-windows))) + (else + (let* ((elements-to-take (if is-single-arg-case + size + (min size (length current-list-segment)))) + (current-window (take current-list-segment elements-to-take)) + (next-list-segment (if (>= step (length current-list-segment)) + '() + (drop current-list-segment step)))) + (collect-windows next-list-segment + (cons current-window result-windows))))))))))) + +(define (%zip l . args) + (chain-apply args + (rich-list (apply map cons (list data l))))) + +(define (%zip-with-index . args) + (chain-apply args + (let loop ((lst data) (idx 0) (result '())) + (if (null? lst) + (rich-list (reverse result)) + (loop (cdr lst) + (+ idx 1) + (cons (cons idx (car lst)) result)))))) + +(define (%distinct . args) + (chain-apply args + (let loop + ((result '()) + (data data) + (ht (make-hash-table))) + (cond + ((null? data) (rich-list (reverse result))) + (else + (let ((elem (car data))) + (if (eq? (hash-table-ref ht elem) #f) + (begin + (hash-table-set! ht elem #t) + (loop (cons elem result) (cdr data) ht)) + (loop result (cdr data) ht)))))))) + +(define (%reduce f) + (if (null? data) + (value-error "rich-list%reduce: empty list is not allowed to reduce") + (reduce f '() data))) + +(define (%reduce-option f) + (if (null? data) + (none) + (option (reduce f '() data)))) + +(define (%take-while pred . args) + (chain-apply args + (let ((result (take-while pred data))) + (rich-list result)))) + +(define (%drop-while pred . args) + (chain-apply args + (let ((result (drop-while pred data))) + (rich-list result)))) + +(define (%index-where pred) + (list-index pred data)) + +(define (%max-by f) + (unless (procedure? f) + (type-error + (format #f "In funtion #<~a ~a>: argument *~a* must be *~a*! **Got ~a**" + %max-by '(f) 'f "procedure" (object->string f)))) + (if (null? data) + (value-error "rich-list%max-by: empty list is not allowed") + (let loop ((rest (cdr data)) + (max-elem (car data)) + (max-val (let ((val (f (car data)))) + (unless (real? val) + (type-error "rich-list%max-by: procedure must return real number but got" + (object->string val))) + val))) + (if (null? rest) + max-elem + (let* ((current (car rest)) + (current-val (let ((val (f current))) + (unless (real? val) + (type-error "rich-list%max-by: procedure must return real number but got" + (object->string val))) + val))) + (if (> current-val max-val) + (loop (cdr rest) current current-val) + (loop (cdr rest) max-elem max-val))))))) + +(define (%min-by f) + (unless (procedure? f) + (type-error + (format #f "In funtion #<~a ~a>: argument *~a* must be *~a*! **Got ~a**" + %min-by '(f) 'f "procedure" (object->string f)))) + (if (null? data) + (value-error "rich-list%min-by: empty list is not allowed") + (let loop ((rest (cdr data)) + (min-elem (car data)) + (min-val (let ((val (f (car data)))) + (unless (real? val) + (type-error "rich-list%min-by: procedure must return real number but got" + (object->string val))) + val))) + (if (null? rest) + min-elem + (let* ((current (car rest)) + (current-val (let ((val (f current))) + (unless (real? val) + (type-error "rich-list%min-by: procedure must return real number but got" + (object->string val))) + val))) + (if (< current-val min-val) + (loop (cdr rest) current current-val) + (loop (cdr rest) min-elem min-val))))))) + +(define (%append l) + (rich-list (append data l))) + +(define (%max-by-option f) + (if (null? data) + (none) + (option (%max-by f)))) + +(define (%min-by-option f) + (if (null? data) + (none) + (option (%min-by f)))) + +(define (%to-string) + (object->string data)) + +(define (%make-string . xs) + (define (parse-args xs) + (cond + ((null? xs) (values "" "" "")) + ((length=? 1 xs) + (let ((sep (car xs))) + (if (string? sep) + (values "" sep "") + (type-error "rich-list%make-string: separator must be a string" sep)))) + ((length=? 2 xs) + (error 'wrong-number-of-args "rich-list%make-string: expected 0, 1, or 3 arguments, but got 2" xs)) + ((length=? 3 xs) + (let ((start (car xs)) + (sep (cadr xs)) + (end (caddr xs))) + (if (and (string? start) (string? sep) (string? end)) + (values start sep end) + (error 'type-error "rich-list%make-string: prefix, separator, and suffix must be strings" xs)))) + (else (error 'wrong-number-of-args "rich-list%make-string: expected 0, 1, or 3 arguments" xs)))) + + (receive (start sep end) (parse-args xs) + (let ((as-string (lambda (x) (if (string? x) x (object->string x))))) + (string-append start (string-join (map as-string data) sep) end)))) + +(define (%to-vector) + (list->vector data)) + +(define (%to-rich-vector) + (rich-vector (list->vector data))) + + +) + +;;; ======================================== +;;; rich-list - Static methods +;;; ======================================== + +(define-object rich-lists + +(define (@range start end . step-args) + (let ((step-size + (if (null? step-args) + 1 + (car step-args)))) + (cond + ((and (positive? step-size) (>= start end)) + (rich-list '())) + ((and (negative? step-size) (<= start end)) + (rich-list '())) + ((zero? step-size) + (value-error "Step size cannot be zero")) + (else + (let ((cnt (ceiling (/ (- end start) step-size)))) + (rich-list (iota cnt start step-size))))))) + +(define (@empty) + (rich-list '())) + +(define (@concat lst1 lst2) + (rich-list (append (lst1 :apply :collect) (lst2 :apply :collect)))) + +(define (@fill n elem) + (cond + ((< n 0) + (value-error "n cannot be negative")) + ((= n 0) + (rich-list '())) + (else + (rich-list (make-list n elem))))) + +) + + + +) +) \ No newline at end of file diff --git a/goldfish/liii/oop_2.scm b/goldfish/liii/oop_2.scm index 301d18a7997fed636e2f1e6a99f3c0a289deac3f..7254962edb1ed1360c921ee18722cb9b277a81ec 100644 --- a/goldfish/liii/oop_2.scm +++ b/goldfish/liii/oop_2.scm @@ -225,21 +225,14 @@ (apply (%this msg) args) (error 'undefined-method (format #f "Method ~a not found" msg)))) - ;; Register all methods - ;; call zero-parameter methods directly + ;; Register all methods as functions (use :apply for all method calls) ,@(map (lambda (method-sym method-name method-def) - (let* ((method-params (if (>= (length method-def) 2) - (cdadr method-def) - '())) - (is-zero-param? (null? method-params))) - (if is-zero-param? - `(varlet %this ,method-name (,method-sym)) - `(varlet %this ,method-name ,method-sym)))) + `(varlet %this ,method-name ,method-sym)) (map caadr instance-methods) instance-method-names instance-methods) - (varlet %this :to-string (%to-string)) + (varlet %this :to-string %to-string) (varlet %this :equals %equals) (varlet %this :apply %apply) @@ -258,7 +251,7 @@ (define (display* . params) (define (%display x) (if (case-class? x) - (display (x :to-string)) + (display (x :apply :to-string)) (display x))) (for-each %display params)) @@ -266,7 +259,7 @@ (define (object->string x) (if (case-class? x) - (x :to-string) + (x :apply :to-string) (s7-object->string x))) ) ; end of begin diff --git a/tests/goldfish/liii/lang-test_2.scm b/tests/goldfish/liii/lang-test_2.scm new file mode 100644 index 0000000000000000000000000000000000000000..97028483b5820cb62a008ef872282394f5ae22de --- /dev/null +++ b/tests/goldfish/liii/lang-test_2.scm @@ -0,0 +1,408 @@ +; +; Copyright (C) 2024 The Goldfish Scheme Authors +; +; Licensed under the Apache License, Version 2.0 (the "License"); +; you may not use this file except in compliance with the License. +; You may obtain a copy of the License at +; +; http://www.apache.org/licenses/LICENSE-2.0 +; +; Unless required by applicable law or agreed to in writing, software +; distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +; WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +; License for the specific language governing permissions and limitations +; under the License. +; + +(import (liii check) + (liii lang_2) + (only (liii base) let1 identity) + (liii cut) + (liii case) + (liii error)) + +(define == class=?) +; (check-set-mode! 'report-failed) + +;; Custom check that uses lang_2's class=? function +(define-macro (check2 expr => expected) + `(check:proc ',expr (lambda () ,expr) ,expected ,class=?)) + +(define check check2) + +(check ((@ + _ 2) 1) => 3) +(check ((@ list 1 _ 3 _ 5) 2 4) => (list 1 2 3 4 5)) +(check ((@ list _ _) 'a 'b) => (list 'a 'b)) + +(check + (let ((a 10)) + (define add (@ + (* a 2) _)) + (set! a 100) + (add 5)) +=> 25) + +(let ((x 5)) + (check + ((@ cons (+ x 1) _) 'y) + => (cons 6 'y))) + +(check (procedure? (@ list 1 2)) => #t) +(check ((@ list 1 2)) => '(1 2)) + +(check ((@ _ 'a 'b) list) => (list 'a 'b)) +(check ((@ map _ '(1 2 3)) (lambda (x) (+ x 1))) => '(2 3 4)) +(check ((@ apply _ '(1 2 3)) +) => 6) + +(check ((@ (@ + _ 1) _) 2) => 3) +(check ((@ _ _) (@ * _ 2) 3) => 6) + +(typed-define (person (name string? "Bob") (age integer?)) + (string-append name " is " (number->string age) " years old")) + +(check (person :age 21) => "Bob is 21 years old") +(check (person :name "Alice" :age 25) => "Alice is 25 years old") +(check-catch 'type-error (person :name 123 :age 25)) + +(check (rich-lists :range 1 5) => ($ (list 1 2 3 4))) +(check (rich-lists :range 1 5 2) => ($ (list 1 3))) +(check (rich-lists :range 1 6 2) => ($ (list 1 3 5))) +(check (rich-lists :range 5 1 -1) => ($ (list 5 4 3 2))) +(check ((rich-lists :range 1 5 2) :apply :collect) => (list 1 3)) +(check ((rich-lists :range 1 5) :map (lambda (x) (* x 2))) => ($ (list 2 4 6 8))) +(check ((rich-lists :range 1 10 1) :map (lambda (x) (+ x 1))) => ($ (list 2 3 4 5 6 7 8 9 10))) +(check (rich-lists :range 5 1 1) => ($ (list ))) + +(check-catch 'value-error (rich-lists :range 1 5 0)) + +(check ((rich-lists :empty) :apply :empty?) => #t) +(check ((rich-lists :empty) :apply :head-option) => (none)) + + +(check (rich-lists :concat ($ (list 1)) ($ (list 2))) => ($ (list 1 2))) +(check (rich-lists :concat ($ (list 1 2)) ($ (list 3 4))) => ($ (list 1 2 3 4))) +(check (rich-lists :concat (rich-lists :range 1 4) ($ (list 3 4))) => ($ (list 1 2 3 3 4))) +(check ((rich-lists :concat ($ (list 1)) ($ (list 2))) + :apply :collect) => (list 1 2)) +(check ((rich-lists :concat (rich-list '(1)) (rich-list '(2))) :apply :count) => 2) + +(let1 result (rich-lists :fill 3 "a") + (check (result :apply :collect) => '("a" "a" "a"))) + +(let1 result (rich-lists :fill 0 "a") + (check (result :apply :collect) => '())) + +(check-catch 'value-error (rich-lists :fill -1 "a")) + +(let1 result (rich-lists :fill 2 42) + (check (result :apply :collect) => '(42 42))) + +(let1 result (rich-lists :fill 1000 "x") + (check (length (result :apply :collect)) => 1000)) + +; (check ($ '(1 2 3) :apply 0) => 1) +; (check ($ '(1 2 3) 0) => 1) + +(let1 lst (rich-list '(1 2 3 4 5)) + (check ((lst :find (lambda (x) (= x 3))) :apply :get) => 3) + (check ((lst :find (lambda (x) (> x 2))) :apply :get) => 3) + (check ((lst :find (lambda (x) (> x 10))) :apply :empty?) => #t) + (check ((lst :find even?) :apply :get) => 2) + (check ((lst :find (lambda (x) (< x 0))) :apply :empty?) => #t)) + +(let1 lst (rich-list '(1 2 3 4 5)) + (check ((lst :find-last even?) :apply :get) => 4) ; 最后一个偶数是4 + (check ((lst :find-last (@ > _ 3)) :apply :get) => 5) ; 最后一个大于3的元素是5 + (check ((lst :find-last (@ > _ 5)) :apply :empty?) => #t) ; 没有大于5的元素 + (check ((lst :find-last zero?) :apply :empty?) => #t) ; 没有0 + (check ((rich-list '()) :find-last even?) => (none))) ; 空列表返回none + +(check ($ (list 1 2 3) :apply :head) => 1) +(check-catch 'out-of-range ((rich-lists :empty) :apply :head)) +(check ($ (list 1 2 3) :apply :head-option) => (option 1)) +(check ((rich-lists :empty) :apply :head-option) => (none)) + +(check ($ (list 1 2 3) :apply :last) => 3) +(check-catch 'index-error ((rich-lists :empty) :apply :last)) +(check ($ (list 1 2 3) :apply :last-option) => (option 3)) +(check ((rich-lists :empty) :apply :last-option) => (none)) + +(let ((lst ($ '(1 2 3 4 5)))) + ;; 基本切片 + (check (lst :slice 1 3 :apply :collect) => '(2 3)) + + ;; from超出范围 + (check (lst :slice 10 3 :apply :collect) => '()) + + ;; until超出范围 + (check (lst :slice 2 10 :apply :collect) => '(3 4 5)) + + ;; from > until + (check (lst :slice 3 1 :apply :collect) => '()) + + ;; 负数索引 + (check (lst :slice -1 3 :apply :collect) => '(1 2 3)) + + ;; 链式调用 + (check (lst :slice 1 4 :map (@ * _ 2) :apply :collect) => '(4 6 8)) + + ;; 空切片 + (check (lst :slice 2 2 :apply :collect) => '()) +) + +(check-true ($ (list) :apply :empty?)) +(check-false ($ '(1 2 3) :apply :empty?)) + +; (check ($ (list ($ 1) ($ 2) ($ 3))) => (($ 1 :to 3) :map $)) + +(let1 lst ($ '(1 2 3 4 5)) + (check (lst :forall (@ > _ 0)) => #t) + (check (lst :forall (@ > _ 3)) => #f) +) + +(check ((rich-lists :empty) :forall (@ > _ 0)) => #t) + +(let1 l (rich-list '(1 2 3)) + (check-true (l :exists even?))) + +(let1 l (rich-list '(1 2 3)) + (check-true (l :contains 1)) + (check-false (l :contains 4))) + +(let ((lst (rich-list '(1 2 3 4 5)))) + (check (lst :reverse :apply :collect) => '(5 4 3 2 1))) + +(let ((lst (rich-list '(a b c d e)))) + (check (lst :reverse :apply :collect) => '(e d c b a))) + +(let ((lst (rich-list '()))) + (check (lst :reverse :apply :collect) => '())) + +(let ((lst (rich-list '(1 2 3 4 5)))) + (check (lst :take -1 :apply :collect) => '()) + (check (lst :take 0 :apply :collect) => '()) + (check (lst :take 3 :apply :collect) => '(1 2 3)) + (check (lst :take 5 :apply :collect) => '(1 2 3 4 5)) + (check (lst :take 10 :apply :collect) => '(1 2 3 4 5)) +) + +(let ((lst (rich-list '(1 2 3 4 5)))) + (check (lst :drop -1 :apply :collect) => '(1 2 3 4 5)) + (check (lst :drop 0 :apply :collect) => '(1 2 3 4 5)) + (check (lst :drop 3 :apply :collect) => '(4 5)) + (check (lst :drop 5 :apply :collect) => '()) + (check (lst :drop 10 :apply :collect) => '()) +) + +(let ((lst (rich-list '(1 2 3 4 5)))) + (check (lst :take-right -1 :apply :collect) => '()) + (check (lst :take-right 0 :apply :collect) => '()) + (check (lst :take-right 3 :apply :collect) => '(3 4 5)) + (check (lst :take-right 5 :apply :collect) => '(1 2 3 4 5)) + (check (lst :take-right 10 :apply :collect) => '(1 2 3 4 5)) +) + +(let ((lst (rich-list '(1 2 3 4 5)))) + (check (lst :drop-right -1 :apply :collect) => '(1 2 3 4 5)) + (check (lst :drop-right 0 :apply :collect) => '(1 2 3 4 5)) + (check (lst :drop-right 3 :apply :collect) => '(1 2)) + (check (lst :drop-right 5 :apply :collect) => '()) + (check (lst :drop-right 10 :apply :collect) => '()) +) + +(check ((rich-list (list 1 2 3)) :apply :count) => 3) +(check ((rich-list (list 1 2 3)) :count (cut > <> 1)) => 2) + +(check ($ '() :apply :length) => 0) +(check ($ '(1) :apply :length) => 1) +(check ($ '(1 2) :apply :length) => 2) +(check ($ '(1 2 3) :apply :length) => 3) +(check ($ '(1 2 3 4 5) :apply :length) => 5) +(check ($ '(1 2 3 4 5 6 7 8 9 10) :apply :length) => 10) + + +(let ((lst (rich-list '(1 2 3 4 5)))) + (check (lst :fold 0 +) => 15) + (check (lst :fold '() (lambda (x acc) (cons x acc))) => '(5 4 3 2 1)) + + (check (lst :fold-right 0 +) => 15) + (check (lst :fold-right '() (lambda (x acc) (cons x acc))) => '(1 2 3 4 5)) +) + +(check ($ '(3 1 2 4 5) + :sort-with (lambda (x y) (< x y))) + => ($ '(1 2 3 4 5))) + +(check ($ (list 1 3 4 2 5) :sort-with < :take 2 :apply :collect) => '(1 2)) + +(check + ($ (list 1 3 4 2 5) + :sort-with < + :take 2 + :apply :collect) + => '(1 2)) + +(check + ($ '((3 . a) (1 . b) (2 . c) (1 . d)) + :sort-with (lambda (x y) (< (car x) (car y))) ;; 按 car 排序 + :apply :collect) + => '((1 . b) (1 . d) (2 . c) (3 . a))) + +;; 测试按绝对值排序 +(check ($ '(-3 1 -2 4 0) :sort-by abs :apply :collect) => '(0 1 -2 -3 4)) + +;; 测试按结构体字段排序 +(let ((people ($ '((name . "Alice") (name . "Bob") (name . "Charlie"))))) + (check (people :sort-by (lambda (p) (string-length (cdr p))) :apply :collect) + => '((name . "Bob") (name . "Alice") (name . "Charlie")))) + +;; 测试空列表 +(check ($ '() :sort-by identity :apply :collect) => '()) + +;; 测试链式调用 +(check ($ '(-3 1 -2 4 0) + :sort-by abs + :filter positive? + :apply :collect) + => '(1 4)) + +;; TODO: rich-hash-table not implemented yet +;; (check (($ '(1 2 3 4 5 6) :group-by (@ modulo _ 2)) :apply :collect) +;; => (hash-table 0 '(2 4 6) 1 '(1 3 5))) +;; +;; (check (($ '(1 2 3 4 5 6) :group-by (@ modulo _ 3)) :apply :collect) +;; => (hash-table 0 '(3 6) 1 '(1 4) 2 '(2 5))) +;; +;; (check (($ '(1 2 3 4 5 6 7) :group-by (@ modulo _ 3)) :apply :collect) +;; => (hash-table 0 '(3 6) 1 '(1 4 7) 2 '(2 5))) +;; +;; (let ((result ($ '("apple" "banana" "cat" "dog") :group-by (@ string-length _)))) +;; (check (result :apply :collect) +;; => (hash-table 3 '("cat" "dog") 5 '("apple") 6 '("banana")))) + +;; Single-argument sliding for rich-list +(check ($ '() :sliding 2) => #()) +(check ($ '(1) :sliding 2) => #((1))) +(check ($ '(1 2) :sliding 2) => #((1 2))) +(check ($ '(1 2 3) :sliding 2) => #((1 2) (2 3))) +(check ($ '(1 2 3 4 5) :sliding 3) => #((1 2 3) (2 3 4) (3 4 5))) +(check ($ '(1 2 3 4 5) :sliding 1) => #((1) (2) (3) (4) (5))) +(check ($ '(1 2 3) :sliding 3) => #((1 2 3))) +(check ($ '(1 2 3) :sliding 4) => #((1 2 3))) + +;; Error cases for size (single-arg) for rich-list +(check-catch 'value-error ($ '(1 2 3) :sliding 0)) +(check-catch 'value-error ($ '(1 2 3) :sliding -1)) +(check-catch 'type-error ($ '(1 2 3) :sliding 1.5)) + +;; Two-argument sliding for rich-list +(check ($ '() :sliding 2 2) => #()) +(check ($ '(1 2 3 4 5) :sliding 2 2) => #((1 2) (3 4) (5))) +(check ($ '(1 2 3 4 5 6) :sliding 2 3) => #((1 2) (4 5))) +(check ($ '(1 2 3 4 5) :sliding 3 1) => #((1 2 3) (2 3 4) (3 4 5) (4 5) (5))) +(check ($ '(1 2 3 4) :sliding 2 2) => #((1 2) (3 4))) +(check ($ '(1 2) :sliding 3 1) => #((1 2) (2))) +(check ($ '(1 2 3 4 5) :sliding 3 2) => #((1 2 3) (3 4 5) (5))) +(check ($ '(1 2 3 4 5 6 7) :sliding 3 3) => #((1 2 3) (4 5 6) (7))) +(check ($ '(1 2 3 4 5) :sliding 5 1) => #((1 2 3 4 5) (2 3 4 5) (3 4 5) (4 5) (5))) +(check ($ '(1 2 3 4 5) :sliding 6 1) => #((1 2 3 4 5) (2 3 4 5) (3 4 5) (4 5) (5))) + + +;; Error cases for step (two-arg) for rich-list +(check-catch 'value-error ($ '(1 2 3) :sliding 2 0)) +(check-catch 'value-error ($ '(1 2 3) :sliding 2 -1)) +(check-catch 'type-error ($ '(1 2 3) :sliding 2 1.5)) + +(check (($ '(1 2 3)) :zip '(a b c) :apply :collect) => '((1 . a) (2 . b) (3 . c))) +(check (($ '(1 2 3)) :zip '(a b) :apply :collect) => '((1 . a) (2 . b))) + +(check ($ '(a b c) :zip-with-index :apply :collect) + => '((0 . a) (1 . b) (2 . c))) + +(check ($ '() :zip-with-index :apply :collect) + => '()) + +(check ($ '(1 1 2 2 2 3 4 5 6 7) :zip-with-index :apply :collect) + => '((0 . 1) (1 . 1) (2 . 2) (3 . 2) (4 . 2) (5 . 3) (6 . 4) (7 . 5) (8 . 6) (9 . 7))) + +(check ($ '(a a b c b) :distinct :apply :collect) + => '(a b c)) + +(check ($ '(1 1 1 2 2 3 3 3 3 5 5 5) :distinct :apply :collect) + => '(1 2 3 5)) + +(check ($ '() :distinct :apply :collect) + => '()) + +(check-catch 'value-error ($ '() :reduce +)) + +(check ($ '(1 2 3) :reduce +) => 6) +(check ($ '(2 3 4) :reduce *) => 24) +(check ($ '(5) :reduce (lambda (x y) (+ x y 10))) => 5) + +(check ($ '() :reduce-option +) => (none)) + +(check ($ '(1 2 3) :reduce-option +) => (option 6)) +(check ($ '(2 3 4) :reduce-option *) => (option 24)) +(check ($ '(5) :reduce-option (lambda (x y) (+ x y 10))) => (option 5)) + +(check ($ '(1 2 3 4 5 6 7) :take-while (@ < _ 5) :apply :collect) => '(1 2 3 4)) +(check ($ '() :take-while (@ < _ 5) :apply :collect) => '()) +(check ($ '(1 2 3) :take-while number? :apply :collect) => '(1 2 3)) +(check ($ '(5 1 2 3) :take-while (@ < _ 3) :apply :collect) => '()) + +(check ($ '(1 2 3 4 5 6 7) :drop-while (@ < _ 5) :apply :collect) => '(5 6 7)) +(check ($ '() :drop-while (@ < _ 5) :apply :collect) => '()) +(check ($ '(1 2 3) :drop-while number? :apply :collect) => '()) +(check ($ '(5 1 2 3) :drop-while (@ < _ 3) :apply :collect) => '(5 1 2 3)) + +(let ((xs ($ '(1 2 3 4 5)))) + (check (xs :index-where even?) => 1) + (check (xs :index-where (@ > _ 3)) => 3) + (check (xs :index-where (@ > _ 5)) => #f) +) + +(check ($ '(1 2 3) :max-by identity) => 3) +(check ($ '((1) (3) (2)) :max-by car) => '(3)) +(check-catch 'value-error ($ '() :max-by identity)) +(check-catch 'type-error ($ '(1 2 3) :max-by "not-function")) +(check-catch 'type-error ($ '("a" "b" "c") :max-by identity)) + +(check ($ '(1 2 3) :min-by identity) => 1) +(check ($ '((1) (3) (2)) :min-by car) => '(1)) +(check-catch 'value-error ($ '() :min-by identity)) +(check-catch 'type-error ($ '(1 2 3) :min-by "not-function")) +(check-catch 'type-error ($ '("a" "b" "c") :min-by identity)) + +(check ((rich-lists :empty) :append (list 1 2)) => ($ (list 1 2))) +(check ($ (list 1 2) :append (list )) => ($ (list 1 2))) +(check ($ (list 1 2) :append (list 3 4)) => ($ (list 1 2 3 4))) + +(check ($ '() :max-by-option identity) => (none)) + +(check ($ '() :min-by-option identity) => (none)) + +; (check (object->string ($ '(1 2 3))) => "(1 2 3)") + +(let1 l (rich-list (list 1 2 3)) + (check (l :apply :make-string) => "123") + (check (l :make-string " ") => "1 2 3") + (check (l :make-string "[" "," "]") => "[1,2,3]") + + (check-catch 'wrong-number-of-args (l :make-string "[" ",")) + (check-catch 'type-error (l :make-string 123 "," "]")) + (check-catch 'type-error (l :make-string "[" 123 "]")) + (check-catch 'type-error (l :make-string "[" "," 123)) +) + +(check ($ (list "a" "b") :apply :make-string) => "ab") +(check ($ (list "a" "b") :make-string " ") => "a b") + +(let ((lst (rich-list '(1 2 3)))) + (check (lst :apply :to-vector) => #(1 2 3))) + +; (let ((lst (rich-list '(1 2 3)))) + ;; TODO: rich-vector not implemented yet +;; (check (lst :apply :to-rich-vector) => (rich-vector #(1 2 3))) +;; (check ((lst :apply :to-rich-vector) :apply :collect) => #(1 2 3))) + +(check-report) diff --git a/tests/goldfish/liii/oop_2_test.scm b/tests/goldfish/liii/oop_2_test.scm index 7fa0098ac1413d92e0564433522ad2893272b4b2..11551262d9835fb5182a466c96d75379a9b4a83d 100644 --- a/tests/goldfish/liii/oop_2_test.scm +++ b/tests/goldfish/liii/oop_2_test.scm @@ -69,14 +69,14 @@ (check (p 'name) => "Alice") (check (p 'age) => 25)) -;; Method calls without parameters (using direct call now) +;; Method calls without parameters (using :apply for zero-parameter methods) (let ((p (person "Bob" 30))) - (check (p :get-name) => "Bob") - (check (p :get-age) => 30) - (check (p :is-adult) => #t)) + (check (p :apply :get-name) => "Bob") + (check (p :apply :get-age) => 30) + (check (p :apply :is-adult) => #t)) (let ((p (person "Charlie" 16))) - (check (p :is-adult) => #f)) + (check (p :apply :is-adult) => #f)) ;; Method calls with parameters (direct call) (let ((p (person "David" 28))) @@ -86,41 +86,41 @@ (let ((acc1 (account "John")) (acc2 (account "Jane" 100)) (acc3 (account "Jack" 200 #f))) - (check (acc1 :get-balance) => 0) - (check (acc1 :is-active) => #t) - (check (acc2 :get-balance) => 100) - (check (acc2 :is-active) => #t) - (check (acc3 :get-balance) => 200) - (check (acc3 :is-active) => #f)) + (check (acc1 :apply :get-balance) => 0) + (check (acc1 :apply :is-active) => #t) + (check (acc2 :apply :get-balance) => 100) + (check (acc2 :apply :is-active) => #t) + (check (acc3 :apply :get-balance) => 200) + (check (acc3 :apply :is-active) => #f)) ;; Method calls that return new objects (let ((p (person "Eve" 20))) - (let ((older-p (p :birthday))) - (check (older-p :get-age) => 21) - (check (p :get-age) => 20))) + (let ((older-p (p :apply :birthday))) + (check (older-p :apply :get-age) => 21) + (check (p :apply :get-age) => 20))) ;; Chain operations (let ((p (person "Frank" 17))) - (let ((adult-p (p :birthday :birthday))) - (check (adult-p :get-age) => 19) - (check (adult-p :is-adult) => #t))) + (let ((adult-p (p :apply :birthday :apply :birthday))) + (check (adult-p :apply :get-age) => 19) + (check (adult-p :apply :is-adult) => #t))) ;; Account operations (let ((acc (account "Grace" 100))) (let ((new-acc (acc :deposit 50))) - (check (new-acc :get-balance) => 150)) + (check (new-acc :apply :get-balance) => 150)) (let ((withdrawn-acc (acc :withdraw 30))) - (check (withdrawn-acc :get-balance) => 70)) + (check (withdrawn-acc :apply :get-balance) => 70)) (let ((same-acc (acc :withdraw 200))) - (check (same-acc :get-balance) => 100))) + (check (same-acc :apply :get-balance) => 100))) ;; Chain operations on account (let ((acc (account "Henry" 100))) - (let ((final-acc (acc :deposit 50 :withdraw 30 :deactivate))) - (check (final-acc :get-balance) => 120) - (check (final-acc :is-active) => #f))) + (let ((final-acc (acc :deposit 50 :withdraw 30 :apply :deactivate))) + (check (final-acc :apply :get-balance) => 120) + (check (final-acc :apply :is-active) => #f))) ;; Type checking tests (check-catch 'type-error (person 123 25)) @@ -138,10 +138,10 @@ ;; Test :to-string method (let ((p (person "Alice" 25))) - (check (p :to-string) => "(person :name \"Alice\" :age 25)")) + (check (p :apply :to-string) => "(person :name \"Alice\" :age 25)")) (let ((acc (account "Bob" 100 #t))) - (check (acc :to-string) => "(account :owner \"Bob\" :balance 100 :active #t)")) + (check (acc :apply :to-string) => "(account :owner \"Bob\" :balance 100 :active #t)")) ;; Test predicate functions (let ((p (person "Charlie" 30))