Oldes / Rebol-wishes

Repository for keeping Rebol related wishes out of issues
0 stars 0 forks source link

DO-COMPARE function #58

Open Siskin-Bot opened 4 years ago

Siskin-Bot commented 4 years ago

Submitted by: BrianH

DO-COMPARE would take two offset series references and three code blocks, and the same /case, /skip and /compare options that are to be standardized in Oldes/Rebol-issues#428 (same as SORT once that's fixed to match). The two series would be compared based on the Oldes/Rebol-issues#428 rules for a single comparison, and depending on the results one of the code blocks would be executed. By "a single comparison", that means that without /skip only the element at the series position is compared, while with /skip only the first span of that record size is compared. No /skip is the same as /skip 1, basically. The Oldes/Rebol-issues#428 rules mean that /all is not required, it's the default.

The three blocks would be for when the comparison finds the first series to be lesser than the second series at that spot, for when they're equal, or for when the first series is greater than the second series. These are the basic conditions that are used by sorting functions like quicksort. Having the three code blocks means that we don't need a /reverse option. The result of evaluating the block chosen is returned from DO-COMPARE.

The /compare option can take an integer or a comparator function. If it's an integer, it will be the (1-based) index of the element in the span to be compared, range-checked from 1 to the /skip size. If it's a function, the function gets called with the values at the positions if no /skip, or the series references at the positions if /skip; returning a positive number or true runs the first (lesser) block, returning 0 runs the second (equal) block, returning a negative number or false runs the third (greater) block. You may need to use a comparator function that returns the numbers if you want stable comparisons, since you need to use the equal block for that.

DO-COMPARE would be used to implement mezzanine database functions that follow the Oldes/Rebol-issues#428 rules, like Oldes/Rebol-issues#1971 and Oldes/Rebol-issues#1972. The underlying code might also be usable by native functions that follow those rules, like those mentioned in Oldes/Rebol-issues#428.

If DO-COMPARE has the same option style as the rest of the Oldes/Rebol-issues#428 functions then it will almost always need to be called using APPLY in mezzanine functions like FIND-MIN and FIND-MAX (#1971) - if we want to avoid that we should convert case, size and comparator to nullable direct parameters. However, if we do that then DO-COMPARE would be more awkward to use directly in user code. If it won't be significantly slower to have the regular options, the consistency would be a usability bonus.

>> do-compare "a" "A" ['lesser] ['equal] ['greater]
== equal
>> do-compare/case "a" "A" ['lesser] ['equal] ['greater]
== greater

>> do-compare "aa" "ab" ['lesser] ['equal] ['greater]
== equal  ; without /skip only the first element is compared
>> do-compare/skip "aabb" "abab" ['lesser] ['equal] ['greater] 2
== lesser  ; with /skip the first span is compared, the whole span compared by default
>> do-compare/skip/compare "aabb" "abab" ['lesser] ['equal] ['greater] 2 1
== equal  ; use /skip /compare integer! to just compare a single element

>> do-compare/skip/compare "aabb" "abab" ['lesser] ['equal] ['greater] 2 3
** out of range error triggered **
>> do-compare/compare "aabb" "abab" ['lesser] ['equal] ['greater] 1
== equal  ; no /skip the same as /skip 1, as far as /compare integer! range considered
>> do-compare/compare "aabb" "abab" ['lesser] ['equal] ['greater] 0
** out of range error triggered **

>> do-compare/compare "a" "b" ['lesser] ['equal] ['greater] :lesser?
== lesser
>> do-compare/compare "b" "a" ['lesser] ['equal] ['greater] :lesser?
== greater
>> do-compare/compare "a" "a" ['lesser] ['equal] ['greater] :lesser?
== greater  ; equal condition not detected by logic! return value
>> do-compare/compare "a" "a" ['lesser] ['equal] ['greater] func [a b] [case [a < b [1] a = b [0] 'else [-1]]]
== equal  ; equal detected by numeric return value

>> do-compare/compare "a" "b" ['lesser] ['equal] ['greater] func [a b] [print [type? a type? b] case [a < b [1] a = b [0] 'else [-1]]]
char! char!  ; with no /skip the element is passed to the comparator
== lesser
>> do-compare/skip/compare "a" "b" ['lesser] ['equal] ['greater] 1 func [a b] [print [type? a type? b] case [a < b [1] a = b [0] 'else [-1]]]
string! string!  ; with /skip the series is passed to the comparator, at the position
== lesser

>> do-compare/case/compare "a" "A" ['lesser] ['equal] ['greater] func [a b] [case [a < b [1] a = b [0] 'else [-1]]]
== equal  ; /compare function! overrides /case, but there's no need to trigger an error here

>> do-compare/case/compare "a" "A" ['lesser] ['equal] ['greater] func [a b] [case [a < b [1] a = b [0] 'else [-1]]]
== equal  ; /compare function! overrides /case, but there's no need to trigger an error here

>> do-compare/compare "a" "A" ['lesser] ['equal] ['greater] func [a b] ["not a number or logic"]
** trigger an error here about the unsupported type or bad value **

Imported from: CureCode [ Version: r3 master Type: Wish Platform: All Category: Native Reproduce: Always Fixed-in:none ] Imported from: https://github.com/rebol/rebol-issues/issues/2110

Comments:


Rebolbot mentioned this issue on Jan 22, 2016: [Epic] Backwards-incompatible API changes, for the greater good [Epic] Holes in our evaluation model


Rebolbot added the Type.wish on Jan 12, 2016