Re: moving min and max to Scheme?
"Dr. Arne Babenhauserheide" <[email protected]> Sun, 12 Oct 2025 23:48:42 +0200
| Newsgroups | gmane.lisp.guile.devel |
|---|---|
| Message-ID | <[email protected]> |
Hi, Tomas Volf <[email protected]> writes: >> I lack the background information about numbers to judge what’s correct >> here. Can you judge that? > > Returning NaN is consistent with Chez, but I am not qualified to say > whether it is correct. I just did basically white-box testing of both > yours and the built-in version and took note of the differences. ^_^ > >> >> It seems like we’ll need a full set of tests for min and max before we >> can replace them. I just realized that we actually have quite a few tests. The following is a slower version, but it passes all tests in our numbers.test. It looks *much* slower, but in my test with one million numbers, it’s factor 2 slower than min in C -- on the slow path. The syntax-case part is faster than C. The tests only check with small numbers of arguments, so only the syntax-case is actually tested, the rest copied over in the hope to make no mistake: (import (ice-9 match)) (define-syntax-rule (define-minmax-expansion f %f compare) (begin (define %f (case-lambda (() (error 'wrong-num-args)) ((x) x) ((x y) (cond ((compare x y) (if (inexact? y) (exact->inexact x) x)) ((compare y x) (if (inexact? x) (exact->inexact y) y)) ((eqv? x y) x) ;; not for -0. 0. ((= x y) (if (zero? x) 0. x)) ;; -0. and 0. (else (nan)))) ((x . rest) (let lp ((m x) (rest rest) (inex? (inexact? x))) (match rest (() (if (real? m) (if inex? (exact->inexact m) m) (error "Wrong type argument" 'wrong-type-arg))) ((x . rest) (cond ((compare x m) (lp x rest (or inex? (inexact? x)))) ((compare m x) (lp m rest (or inex? (inexact? x)))) ((eqv? m x) (lp m rest (or inex? (inexact? x)))) ;; not for -0. 0. ((= m x) (lp (if (zero? m) 0. m) rest (or inex? (inexact? x)))) ;; -0. and 0. ;; neither > nor < nor = means (nan): nothing more to do (else (nan))))))))) (define-syntax f (lambda (stx) (syntax-case stx () (f (identifier? #'f) #'%f) ((_) #'(error "Wrong number of arguments to" 'wrong-num-args)) ((_ x) #'(if (real? x) x (error "Wrong type argument" 'wrong-type-arg))) ((_ x y) #'(let ((x* x) (y* y) (inex? (or (inexact? x) (inexact? y)))) (cond ((compare x* y*) (if inex? (exact->inexact x*) x*)) ((compare y* x*) (if inex? (exact->inexact y*) y*)) ((eqv? x* y*) x*) ;; not for -0. 0. ((= x* y*) (if (zero? x*) 0. x*)) ;; -0. and 0. (else (nan))))) ((_ x . y) #'(f x (f . y)))))))) (define-minmax-expansion max %max >) (define-minmax-expansion min %min <) > I believe this simplifies it a bit too much. The 2019 version (well, > the draft, I am not buying the final document, and consider the very > need to *buy* a standard to be absurd) specifies both `minimum' and > `minimumNumber' with different behavior regarding NaNs (section 9.6). I > do not know whether r7rs prescribes which of those shall be used, but > probably not. I don’t think it does. >> But from r7rs: >> >> An arithmetic operation where one operand is NaN >> returns NaN, unless the implementation can prove that the >> result would be the same if the NaN were replaced by any >> rational number. >> >> Are min and max arithmetic operations? > > ¯\_(ツ)_/¯ :-) I think this means: don’t change behavior. >> Do I need to import something? > > Ah, my bad, it is an extension[0] from my library, it helps to make > large numbers more readable. It is sourced automatically on REPL start > and I did not realize I have used it. You can just drop all of the _, > including the #_ prefix, and it will work. Sorry about that. > > 0: https://git.wolfsden.cz/guile-wolfsden/tree/wolfsden/x/underscore.scm No worries, that looks pretty useful. > In all the above I see large variances in GC time, so I wonder whether > that had some effect on the result. In my tests I have always executed > ,gc first, so you can see the GC is always 0 (min/max should not really > need to trigger GC anyway). I am unsure how large the effect of that is > though, possibly not large. In the benchmarks, gc made a difference for the very large lists. So it may have some optimization to do. Best wishes, Arne -- Unpolitisch sein heißt politisch sein, ohne es zu merken. draketo.de
signature.asc
(application/pgp-signature, 1.1 KB)
-----BEGIN PGP SIGNATURE----- iQJEBAEBCAAuFiEE801qEjXQSQPNItXAE++NRSQDw+sFAmjsIjoQHGFybmVfYmFi QHdlYi5kZQAKCRAT741FJAPD610JEACcEFqb/XG1ES+m59DZpq+FjixJlOhSyfLP 024ugWD7U5up8znQhYkjhSs6QtPP0aAZV4H1NYd8ArtjC9/YB49yZOEzom6oAaQv W/0+tfEILjeiTC8fU1K3YsRj7hazkmw+9eSjjQBc7Nqf1qHjy9OVaDC3WkqUJBJM oPH8rTQ9gNu03bOSv3WUZV2Ch66y5JfT1ElwkvS9eICb2xNtT7oTiBjrQdGwYepd EI6gRFBfkd8+y0CftjQm9vRl+VbUa4zVGMO/EOwyksPDUo6XdvNKsa0vfKfwhCpH J1j15VSxTFMKpGF/ljgq2Zp8+N/X30ixNsovSDwM8gjCaCiU4ulMTN//9mzzLIPy D6Tn293GF/Fnt4hdsW7v3g3C8Z0khRxu/fhswf6NAE4e/Q8TMtquFaJvnV/2u3fP b+dCQbqtr/KyGnWyroeSD1FUXfj0ym9aIMTuVpFLPFY0/fYAImUitFmEfb8qOhub BcEDLR5E89K+sCBR/voGA6mPfRViuwJEQvorEQsCnllg76FozU0RsL+Z59r0qNB6 8h2ajS092UAnmaYIpHe45YMRl8DyfA7F5JoFMjvd+rMsmIl5V8R4L4UTlAg0kuIy oBAPpuBQWvuGp4QwrnKmYwrpFB/WGEojzEfxGa2Qaq5UDn8w+cU41mRcZuyPZfgI Wv9/icp99YjEBAEBCAAuFiEE3Si95tmHXKvOSosd3M8NswvBBUgFAmjsIjoQHGFy bmVfYmFiQHdlYi5kZQAKCRDczw2zC8EFSKIzBACaxCGna+vVLnii6TJnwtwQ54xN BIVF8pefNYIlEif2VRhCLKpEjgkur615FSkYl3BRO4PoMWdlHMdQI9ODWr3ZReGo RhcVz131P991BoUrD3Jx3JhSPccIhGrGWqjSiOe5+IGDQRhDxvl4hZamgyIaycw9 dAeuBt4YNYxHfD3EWw== =9Au3 -----END PGP SIGNATURE-----