We don't bite newbies here... much PerlMonks

by Discipulus (Monsignor)
 on Feb 11, 2013 at 13:50 UTC Need Help??

hello there,

I want share an headache with you due to the Perl's loop control until structure.
I'm currently happily reading, with some profit i hope, High Order Perl and so far, apart from some math base i do not have, i can quite afford it. and i very like it.

While explaining iterators apllied to permutations the author, with the usual kindness, come back a little to present a simpler problem: an odometer.

Realized that the odometer is that set of little wheels showing how many kilometers my kawasaki GPZ 750 divoured in his history, i looked at the subroutine proposed with a "ah ok... is very simple. i can get it.." approach.

This line, clear in the result wanted, mazed and puzzled me:

```<P>

until (\$odometer[\$wheel] < 9 || \$wheel < 0)<P>

This is the original code of the sub with a little prepended code just to call it, putted by me.
```use strict;
use warnings;

my @odometer = qw(0 0);
for (1..\$ARGV[0]) {
print "was: @odometer\t";
@odometer = increment_odometer(@odometer);
@odometer ? (print join ' ', @odometer) : (print "No more Wheels t
+o turn! \$!" and exit) ;
print "\n";

}

sub increment_odometer {
my @odometer = @_;
my \$wheel = \$#odometer; # start at rightmost wheel

until (\$odometer[\$wheel] < 9 || \$wheel < 0)
{
\$odometer[\$wheel] = 0;
\$wheel--; # next wheel to the left
}
if (\$wheel < 0) {
return; # fell off the left end; no more sequences
} else {
\$odometer[\$wheel]++; # this wheel now turns one no
+tch
return @odometer;
}
}

### The semantic translated

Now come in count another think, i'm not eng-native (as my name suggest i speak latin..).
Every word in english have an attached logical translation in my mind and, while i read some perl code, this is used as logical unit to build up the scenario: i cannot think in english..

I know that until simply invert the condition of the same while loop.
The while in my native language is mentre and the reverse until is 'finché'.
But while the first transaltion fit well in any circumstance, the second does not apart from trivial case of the condition.
I took the english-italian dictionary and i found that until is translated as finché (non)'. This beacuse in english until want a positive sentence but in italian 'finché bring the possibility to use a negative form of the sentence after it, and in this way is commonly used: i wait until you arrive can be translated as aspetto finché arrivi but also sounds good as 'aspetto finché NON arrivi'.
In italian the double negation is admitted and correct: Non c'è nessuno is There is nobody but have a double negation ('non' and 'nessuno') but sound as 'there is not nobody'.

### Inverting the condition

Naturally the original code is correct (this was never in doubt) but now i want a valid logical representation of it in my mind so i played with the line with the until condition:
```until (\$odometer[\$wheel] < 9 || \$wheel < 0) #ok original

#negated direct form also ok
while  ( !(\$odometer[\$wheel] < 9 || \$wheel < 0) )

while (\$odometer[\$wheel] > 9 || \$wheel < 0) # NO

while (\$odometer[\$wheel] > 8 || \$wheel < 0) # OK but NOT for greter th
+an limit of the odometer!
#Use of uninitialized value in numeric eq (==) at C:\SCRIPTS
+\odometer.pl line 27.
#Modification of non-creatable array value attempted, subscr
+ipt -3 at C:\SCRIPTS\odometer.pl line 26.
#getting: 9 9->

while (\$odometer[\$wheel] == 9 || \$wheel < 0) # OK  but NOT for greter
+than limit of the odometer!
#Use of uninitialized value in numeric eq (==) at C:\SCRIPTS
+\odometer.pl line 27.
#Modification of non-creatable array value attempted, subscr
+ipt -3 at C:\SCRIPTS\odometer.pl line 26.
#getting: 9 9->

while (\$odometer[\$wheel] == 9 && \$wheel >= 0)  # OK

It does not appear the first attempt using an LABEL if redo solution because i think i'm too young to use LABELs.

But really the negation of (\$odometer[\$wheel] < 9 || \$wheel < 0) is (\$odometer[\$wheel] == 9 && \$wheel >= 0) ?

This is not a critic to Perl or about the choice of words in the language or a request to abolish the poor until is only something i want to share about the problematic enlace between a logical language and a spooken one.

L*

UPDATE 10 Nov 2017 found a similar article at blogs.perl.org and left here my comments and a link to this post.

there are no rules, there are no thumbs..
Reinvent the wheel. Then learn The Wheel and use it!

Replies are listed 'Best First'.
by tmharish (Friar) on Feb 11, 2013 at 14:10 UTC

I cant say much about the semantics of until in the context that you have described it as neither English nor Italian are my native languages.

However I believe that the use of until and unless and the likes are dangerous for precisely this reason. I find it far better to use while( ! ) and if(!) for complex conditions.

Regarding the negation - You need to use De Morgan's laws for that negation. It essentially states:

```NOT( A OR B ) <==> NOT(A) AND NOT(B)
NOT( A AND B ) <==> NOT(A) OR NOT(B)

So
```NOT( (\$odometer[\$wheel] < 9 || \$wheel < 0) )
==>
NOT(\$odometer[\$wheel] < 9 ) AND NOT (\$wheel < 0)
==>
\$odometer[\$wheel] >= 9 AND \$wheel >= 0
by BrowserUk (Pope) on Feb 11, 2013 at 14:31 UTC

The first thing I noticed is that the ordering of the sub conditions is probably wrong in the original.

As constructed, if \$wheel < 0, it won't be detected until after the first sub condition has already accessed a negative subscripted array element, which is almost certainly wrong.

So, I'd suggest you start with switching that around: until( \$wheel < 0 || \$odometer[ \$wheel ] > 9 ) { ....

Then, I'd suggest you translate that into prose in your preferred language. (I use English; for you probably Italian?).

Loop, until the array index reduces past the start of the array; or, the value of the array element at the current index is greater than 9.

Then invert the logic of that prose description:

Loop, while the array index hasn't reduced past the start of the array; and, the value of the array element at the current index is less than or equal to 9.

And then translate it back to code: while( \$wheel >=0 && \$odometer[ \$wheel ] <=9 ) {

And finally, the inevitable question about why you want to do that anyway?

How do Italian recipes phrase the common situation: Bake for 15 minutes or until the cheese is melted and bubbling.

With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
And finally, the inevitable question about why you want to do that anyway?

Interestingly this is not as straight forward as one might assume. In have studied cognitive development in children for the purpose of AI and it turns out that thinking in negation is a higher order cognitive function that children develop much later - This of course translates into the effort that one needs to put in when analyzing logic ( or code ). ( Basic papers on Google Search )

In terms of programmers I have actually seen, even experienced programmers, ( over 10,000 hours of programming ) make mistakes when reading other peoples code that contains negation of large expressions. Of course they can write them very easily.

I have seen a similar argument put forth in the book Perl Best Practices

In have studied cognitive development in children for the purpose of AI ...

I cannot argue against your claimed experience. But ...

thinking in negation

I can counter that.

Waiting, or repeating something until something happens; is not thinking in negation.

However, waiting or repeating something while something has not happened; is!

even experienced programmers, ... make mistakes [when] code that contains negation of large expressions.

I'll take it that by "large expressions" you really mean "complex expressions".

And that points up the error in this justifiction. until (used properly) does not contain a negation.

until (used properly), captures the positive expression of the terminating condition.

Eg. Contrast:

• until(  eof ) { Loop until you get to the end of file.
• while( !eof ) { Loop while you haven't reached the end of the file.

Which requires "thinking in negation"?

I have seen a similar argument put forth in the book Perl Best Practices

Never in the history of programming, has so much, been screwed up for so many; by so little justification.

This is far from the worst atrocity that book has inflicted.

With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
This because in english until want a positive sentence but in italian 'finché' bring the possibility to use a negative form of the sentence after it, and in this way is commonly used: 'i wait until you arrive' can be translated as 'aspetto finché arrivi' but also sounds good as 'aspetto finché NON arrivi'.

Which I read as "I will wait until you have not arrived".

Translating via prose doesn't work well when that prose is invalid in a boolean sense due to slang, double negation or whatever.

In italian the double negation is admitted and correct:

I don't speak Italian, but I do note the OP said:

In italian the double negation is admitted and correct:

I do know that in English, there are some sentiments that cannot be adequately captured without a double negation: Eg.

I like toast. does not fully capture the semantics of I don't not like toast. (Or perhaps more grammatically correct; I don't dislike toast.)

I cannot attempt to divine the true sentiments of the double negation in a language I do not speak; but from my limited experience of other non-English languages that have constructions that make little or no sense when translated literally to English, I know that often such constructions are perfectly logical in that language.

Eg. When speaking English, a Dutch person (who are usually very competent in English) will often say "When you were a woman..." instead of "If you were a woman ..." and conversely, "If the baby is born ... " when they actually mean "When the baby is born ...". It all comes down to the duality of the Dutch words 'als'.

Similarly, expressing 8:35 in English as 5 minutes past half an hour before 9:00 makes no sense at all; but that is exactly how it is commonly expressed in Dutch: vijf over half negen.

Which I read as "I will wait until you have not arrived".

Basically, your attempts to apply non-native logic to a language you do not speak doesn't make for a sound basis of argument.

With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
BrowserUK: unfortunately your example until( \$wheel < 0 || \$odometer[ \$wheel ] > 9 ) result in a broken odometer was: 0 0        No more Wheels to turn!.
Even in the negated form.

I have no reason to do this (and the author of the book state introducing the permutation problem that is a meaningless problem); i'm learning and when i encountered this idiom, the headache begun.. because it was not translatable in the, poor, logic in my brain. When I code i simply avoid thinking in this way and, hopefully, ther more way to do it..

Finally the cook sentence you propose is a trivial case in which the plain 'finché' translation is okay. You miss the point: when you use a keyword as a logical operator and this word have a translation in your language it can create some semantic headache.

Anyway thanks for the interest you put in such anomalous meditation.

L*
there are no rules, there are no thumbs..
Oddly enough, the \$wheel < 0 may be unnecessary even if it does aid in clarification.

Only in the case of the odometer 'turning over' will \$wheel go negative. When \$wheel is -1, \$odometer[ \$wheel ] will be the same as \$odometer[ \$#odometer ], which will have already been set to 0.

... of course relying on this in your code will piss off your maintainers.

TJD

Oddly enough, the \$wheel < 0 may be unnecessary

Indeed.

When I originally posted my suggestion, it was purely on the basis of reading the code snippet in the OP. In general, when doing this kind of iteration with a while loop, I check the range of the index before attempting to use it to access the array.

Only after discipulus refuted the idea, did I download the snippet and play with it and found that as posted, it "worked okay".

It took a little more investigation to understand that it gets away with it because of perl's propensity to accept negative array indices.

The funny thing is, that in one way, coding it "wrongly" has a benefit:

• Coded 'correctly' attempts to iterate beyond the scale of the odometer fail silently:
```1
2
3
...
97
98
99

• But coded 'wrongly', that beyond range attempt fails noisely:
```1
2
3
...
98
99

Use of uninitialized value within @odo in numeric lt (<) at C:\test\ju
+nk31.pl line 9.

Use of uninitialized value within @odo in numeric lt (<) at C:\test\ju
+nk31.pl line 9.

which could be seen as an advantage :)

Of course, there are better ways of detecting the failure...

With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
by AnomalousMonk (Chancellor) on Feb 12, 2013 at 01:41 UTC
BrowserUk: The first thing I noticed is that the ordering of the sub conditions is probably wrong in the original.

Just so. Errata lists for HOP available here. Second printing list discusses this error on pgs. 132-133 (didn't check first printing).

by Anonymous Monk on Feb 12, 2013 at 02:49 UTC
until(godot) {wait();}
by sundialsvc4 (Abbot) on Feb 12, 2013 at 19:35 UTC

I’ve never had any problems wrapping my head around the concept of until.   And, I never particularly agreed with the language-purists who decried, say, the repeat .. until statement in Pascal.   As long as you clearly understand how a particular language implements the concept, it’s a perfectly sensible idea that we use all the time:   “get on the freeway and then keep driving until you get there.”   “Jump rope until you get tired of jumping.”   A programming language should easily support this very common notion.   The people who correctly point out that it is can be equivalently expressed in terms of while are stating a theoretically-correct point, but not a pragmatic one.   A language is a tool, built for me to get a (\$\$)job(\$\$) done.   I don’t have to turn my thinking upside-down ... that’s what compilers are for.   It can “figure it out” well enough either way; therefore, it should.

Create A New User
Node Status?
node history
Node Type: perlmeditation [id://1018143]
Approved by Corion
Front-paged by MidLifeXis
help
Chatterbox?
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (3)
As of 2018-04-21 18:01 GMT
Sections?
Information?
Find Nodes?
Leftovers?
Voting Booth?
My travels bear the most uncanny semblance to ...

Results (81 votes). Check out past polls.

Notices?