How to use "USE cmpfunc"? And what's up with SEARCH?

Started by lettersquash, April 01, 2020, 01:07:24

Previous topic - Next topic

lettersquash

Some commands, e.g. SEARCH and SORT have this option at the end: USE cmpfunc, with the instruction that the function must return particular values, but I don't understand how to use it. Could someone explain or post an example that shows how to please?

The editor help on SEARCH is a little confusing, as it says
SEARCH A, key, BYREF ridx [USE cmpfunc]
and

1. I'm not sure what it means by 'key' - I can only find values so far, maybe I'm doing it wrong. Are dictionary arrays like this
a={"one":1,"two":2,"three":3}
? a.two
search a, two, ridx
? ridx
search a, "two", ridx
? ridx

because that prints
2
-1
-1

i.e. it doesn't find the key "two"
I can't find anything on key-value pairs in the various help pages, but I have a vague memory they're like the above.

2. the word "BYREF" in capitals doesn't help, because as far as I can tell, it's not meant to be put in as a literal statement, like (presumably) "USE", or, in other commands, "COLOR", etc., but indicates that it will automatically operate as byref, or must be defined as byref in the target function.
I'll have you know, I'm coding all the right commands, just not necessarily in the right order.

bplus

Wow, never seen that before. SORT gives us a clue about the Compare Function. Looks like you specify Ascending or Descending Sort by that.

FUNC qscmp(x,y)
IF x=y
    qscmp=0
ELIF x>y
    qscmp=1
ELSE
    qscmp=-1
ENDIF
END
...
DIM A(5)
FOR i=0 TO 5
    A(i)=RND
NEXT
SORT A USE qscmp(x,y)  'gscmp is a cmpFunc or Compare Function

for i in a do ? i  '>>> result ascending sort
1 person likes this

bplus

This example under SEARCH makes no sense to me:

FUNC cmp(x,y)
  cmp=!(x=y)  'return 0 if match and 1 if no match ???
END
...
DIM A(5)
FOR i=0 TO 5
    A(i)=5-i
NEXT

' Search Array, Key, return Index that doesn't match???
SEARCH A, 4, r USE cmp(x,y)
PRINT r:REM prints 1
PRINT A(r): REM prints 4

Someone got too fancy for their pants ;-))

I'd build my own search function.
1 person likes this

lettersquash

Oh, I see. That makes sense and works fine (also reversing the -1 and 1 does the descending sort). It's a bit unclear what's going on. I'm not sure three results are needed if it's comparing x and y as pairs of consecutive values, like a shuffle-sort: I'd expect it just needed swap or don't swap.

Anyway, that's a good clue. SEARCH finds the first occurence of a given value (where it says key) from what I've tried, putting the return index in the next variable, ridx. What BYREF has to do with it, I don't know, and the cmpfunc choices are specified, zero if x=y, non-zero if x<>y. But I don't know what it would be comparing.

I see you've just posted about search. I'll have a look. Cheers.
I'll have you know, I'm coding all the right commands, just not necessarily in the right order.

lettersquash

#4
Blimey, yes. Fancy pants indeed.
cmp= !(x=y)
means return true if x<>y, false if x=y
but whether that's about a match, no match, I'm not sure.

Your results are as expected though - r is the index of where it found the "key" (value): 4, which is index 1, the second element (zero based), and A(r) is just using the index and asking what's there - which is 4, as you searched for that!

If the "key" (value) wasn't there, it would give -1 as a fail (one less than the base).

Bizarre...I mean, the cmpfunc is puzzling, the SEARCH command is fine. It just ought to say something more like
SEARCH array, value, ridx
...unless we're missing something and it will find actual keys in key-value pairs.
I'll have you know, I'm coding all the right commands, just not necessarily in the right order.

bplus

BYREF means the main calling code shares the variable value with the sub, it's a way of passing a result to calling code so the calling code can use that value, most parameters in SmallBASIC are passed by value which means they are thrown away by calling code after the call.

Search might normally be a function that returns the index of the find or -1 or -99 if no match found. With 0 base arrays as default you don't want to return 0 usually as a NOT found signal.
1 person likes this

lettersquash

Yes I get that, but then you ought to put BYREF in the function definition in front of x and y, surely? Or maybe the help saying BYREF just indicates that you don't have to, and it'll return a shared value. You (or whoever) didn't put
FUNC qscmp(byref x, byref y)
and it worked.

Yes - if you switch to a base 1 for the array, a fail will then give 0. Always LBOUND(A)-1. I don't think it's on the default base, come to think of it, so if you have normal default, but say
dim j (14 to 56)
then a failed search will give 13.
This language is pretty cool. 8)
I'll have you know, I'm coding all the right commands, just not necessarily in the right order.

bplus

Another try explaining BYREF.

When you pass a variable BYREF you intend to use a new value the procedure assigned to. If you pass a variable as default BYVALUE you don't have to worry if the procedure changed the value while using it, it will be the same as before the call.

SEARCH would likely get upset if you used a number like 10 for the BYREF variable.

Update: Yeah "Type Mismatch" error for the number 10.
1 person likes this

lettersquash

Quote from: bplus on April 01, 2020, 03:24:41
Another try explaining BYREF.

When you pass a variable BYREF you intend to use a new value the procedure assigned to. If you pass a variable as default BYVALUE you don't have to worry if the procedure changed the value while using it, it will be the same as before the call.

SEARCH would likely get upset if you used a number like 10 for the BYREF variable.

Update: Yeah "Type Mismatch" error for the number 10.
No mate, I've confused you somehow. I understand byref and byvalue, although I tend to eschew the idea of scopes, local and private variables and so on altogether in my programming: if I name something, I don't want it having different values in different places - old skool BBC geezer. My point was really one about the misleading text in the help - I can't remember what these are called, but where placeholders are used with square brackets for optional parameters, etc. And just to be clear, I'm talking about the little help texts from F1, which are mostly the same as those on the web if you hit F2, with the caret in the particular keyword. The convention seemed to be established in most of these that a word in capitals was an actual keyword to be inserted (if that option is chosen). So, for example, you can say (something like - my syntax may not be perfect here)...
RECT x, y, x1, y1, c
or
RECT x, y, STEP w, h, c
or
RECT x, y, STEP w, h COLOR c
or RECT STEP x, y, STEP w, h, c FILLED
or RECT x, y, x1, y1 COLOR c FILLED
The help thingy summarizes that with the options in brackets and using pipe for OR:
RECT [STEP] x, y [,|STEP x2, y2] [, color| COLOR color] [FILLED]

Indeed, "color" there in lower case is understood to be the variable name of a colour, while COLOR is a keyword to be used if you choose that form (although it can be lowercase - hehe).

But in SEARCH, we have
SEARCH, A, key, BYREF ridx [USE cmpfunc]

so the convention is flouted, because it suggests that one should write:
SEARCH myArray, a_key, BYREF index
and, as you demonstrate, you do put USE literally if you add a cmpfunc. I spent a fair while writing variations on "...BYREF var..." before the errors made me realise this isn't a keyword to be inserted in the sending code.

You are also adding a confusion, I think, about BYREF and the error of 'Type Mismatch' you get when you try to send 10. Whether a function returns a variable by reference or variable is a property of the function (or subroutine - this applies to both, I think), and is therefore usually defined in the function definition, as per the example I gave last time. If, as in sB, functions return variables byvar by default, then you specify for the particular variables you want as byref:
FUNC myF(BYREF q, z)
In that, z is byvalue, per default for smallBASIC functions. The sending text doesn't prescribe which method to use (as far as I know, in sB...it may be possible in other languages).

Now, again, just a critique of the help text, why does it say BYREF at all in the "example summary of a sending code", when, as far as I remember, you never use it in a line of sending code. You send just a variable name or other expression.

I think the Type Mismatch error for 10 is simply because the executor expects a variable name only within that particular command, not any kind of literal, raw data, numeric or string. Normally, you could send literal values to a function, but this isn't such a case, it's within a particular command with its own syntactic structure, and what you have to put there is a variable name.

I'm beginning to think that BYREF got put in that help text just as a way to say that the variable will be changed whether you like it or not (it would be awkward dealing with the result if it didn't).
I'll have you know, I'm coding all the right commands, just not necessarily in the right order.

Aurel [banned]

Hello experts
BYREF means by pointer or INT(address)
and there is no chance if you define
myfunc(byref a,b) that one of parameter is by value (byVal)
so both of them are pointered. :D
(Y)

lettersquash

Just as a friendly rant from a dinosaur, this is one of my pet peeves about programming languages, byref/byvalue, local variables, objects and all that. I think it can create as many unnecessary complications and errors as the much-maligned GOTO, but maybe that's partly because I never used it in decades of programming in BBC BASIC (an early version). Local variables, scopes, all that, didn't exist. If you sent a variable to a FN or PROC and it got changed, that was the point of sending it! If you wanted a copy of it inside or outside a FN or PROC, you just copied it inside or outside the routine, giving it a new name. Simples.

So now, in "modern" languages (like 19 year old ones!), I mostly try to avoid functions and subroutines with parameters, although sometimes it's the best thing to do. It's often avoidable just by setting your global variable to whatever you want it before sending flow to the subroutine instead of putting it in the parameter.

x=4
dostuff
...
x=27
dostuff

is equivalent logically to

dostuff(4)
...
dostuff(27)

except in the latter case you have to remember whether the language uses byref or byvalue as default. And it still seems mostly a silly idea to have the subroutine copy the incoming variable, do stuff to the copy, and lose the original unless you specify otherwise!

Don't get me started on OOP!
Where's that nurse? It's time for my meds.
</rant>
I'll have you know, I'm coding all the right commands, just not necessarily in the right order.

lettersquash

Quote from: Aurel on April 01, 2020, 12:50:22
Hello experts
BYREF means by pointer or INT(address)
and there is no chance if you define
myfunc(byref a,b) that one of parameter is by value (byVal)
so both of them are pointered. :D
Then explain this:
x=1:y=2
j=dostuff(x,y)
? "after",x,y
end

func dostuff(a,byref b)
a++
b++
? "inside",a,b
dostuff=0
end func
I'll have you know, I'm coding all the right commands, just not necessarily in the right order.

lettersquash

Hint:
inside      2    3
outside    1    3
I'll have you know, I'm coding all the right commands, just not necessarily in the right order.

Aurel [banned]

same result i get in oxygen basic with your code:
(Y)

Aurel [banned]

and result of local variable returned as function value
(Y)