Popular Elephant Traps #1: Scalars, empty vectors, and vectors of length one
Contents
Inspecting a value
Novices are tripped up by the silliest things.
And not just novices... though we don't care to admit it.
We may even be unconscious of stumbling, because we pick ourselves up so quickly and carry on without it breaking our train-of-thought.
An observational study in the 1970s showed that expert programmers made just as many silly mistakes as novices, but recovered from them far more quickly. Sometimes so quickly the stumble could only be detected by stepping frames in the video record.
A common stumble, for me, is to assume I've got a scalar noun when really it's a vector of length 1, or sometimes length 0. Most of the time I know what I've computed. But occasionally, I code a T block badly inside if. T do. ...
Because if. T do. is so forgiving, it hides my bug, which thereby becomes an elephant trap.
An elephant trap is a big hole dug in a little-used pathway and covered with branches so that only great heavy beasts will fall through.
Such as a senior executive working late Sunday night to get the quarters' figures out for Monday morning.
Ever had a 2AM phone call...?
Here's some examples:
if=: 3 : 'if. y do. ''TRUE'' else. ''FALSE'' end.'
This lets us try out sample T blocks for an if. T do. statement:
if 1 TRUE if ,1 TRUE
...well, we'd jolly well hope so.
But note this...
if 0 1 1 FALSE if 1 0 1 TRUE
This is because, to quote http://www.jsoftware.com/help/dictionary/ctrl.htm ...
The last sentence executed in a T block is tested for a non-zero value in its leading atom, ...
Well, shouldn't we expect trouble if we give if. T do. a T consisting of several ones-and-zeros instead of just one?
It can only happen to a rabbit.☺
But the problem turns up in disguise, like when we forget to match two strings with (-:) and use (=) instead:
if 'abba' = |.'abba' NB. test for a palindrome TRUE if 'xbba' = |.'abba' FALSE
Fine -- it works. Let's stick it in the code.
But - hey! - what about this? ...
if 'abba' = |.'xbba' TRUE
What's going on?
'abba' = |.'xbba' 1 1 1 0 if 'abba' = |.'xbba' TRUE 'xbba' = |.'abba' 0 1 1 1 if 'xbba' = |.'abba' FALSE
...oh, it's only that leading zero again.
All we need to do is take care to use (*./) when faced with possible boolean vectors in T blocks, like this:
*./ 1 0 1 0 *./ 1 1 1 1
...don't we?
But hows about this:
b=: 1 0 1 if *./b FALSE b=: 1 1 1 if *./b TRUE b=: i.0 if *./b TRUE
More alarmingly:
if a: TRUE if 0$0 TRUE if '' TRUE
These likewise turns up in disguise:
if 'x'= 'xbba' -. 'ab' TRUE
...just what we expect.
But - hey! - do we expect this? ...
if 'x'= 'xbba' -. 'abx' TRUE
It's all because:
$ 'x'= 'xbba' -. 'abx' 0
Here's another gotcher in the same vein:
b=: 0 if b }. 0 0 0 0 0 FALSE b=: 4 if b }. 0 0 0 0 0 FALSE b=: 5 if b }. 0 0 0 0 0 TRUE
A test tool for routine use
Much trouble stems from the fact that simply typing a noun is a bad guide to its value.
'a'
a
,'a'
a
' '
,' '
''
i.0
' '
'abcde'
abcde
,:'abcde'
abcdeYet we do it all the time.
(Even experts. Because it's so easy -- so we don't have to break our chain-of-thought.)
The aforementioned elephant traps can be detected in time if we routinely inspect our working nouns a little more carefully than that. The operative word is routinely. We don't want to undergo Zen master training to learn to apply routine checks that would otherwise break our concentration. J skills should not be an occupational deformity.
It would be nice if a status bar in the J Front End showed the basics about the last value calculated. Such as shape and datatype, as well as a rough idea of the contents which entering the bare name or phrase gives us. Failing that, we need an easy tool.
I find the following tool useful. In fact I use it all the time:
qq=: 3 : 0
_. qq y
:
NB. Shows '=' if (x-:y) else '~'
NB. Or you can label output with x if x starts with: '"'
what=. datatype, '/$y:', [: ": $ NB. identify y type/shape
if. 1-: 128!:5 x do. NB. only if a monadic call...
x=. '' NB. shrink x to nothing
else. NB. assume x is a comparison value
x=. (y-:x) { '~='
end.
,. (x,what y) ; <y
)Here's qq in use with the above sample values:
qq 'a' ┌───────────┐ │literal/$y:│ ├───────────┤ │a │ └───────────┘ qq ,'a' ┌────────────┐ │literal/$y:1│ ├────────────┤ │a │ └────────────┘ qq ' ' ┌───────────┐ │literal/$y:│ ├───────────┤ │ │ └───────────┘ qq ,' ' ┌────────────┐ │literal/$y:1│ ├────────────┤ │ │ └────────────┘ qq '' ┌────────────┐ │literal/$y:0│ ├────────────┤ │ │ └────────────┘ qq i.0 ┌────────────┐ │integer/$y:0│ ├────────────┤ │ │ └────────────┘ qq ' ' ┌────────────┐ │literal/$y:5│ ├────────────┤ │ │ └────────────┘ qq 'abcde' ┌────────────┐ │literal/$y:5│ ├────────────┤ │abcde │ └────────────┘ qq ,:'abcde' ┌──────────────┐ │literal/$y:1 5│ ├──────────────┤ │abcde │ └──────────────┘
Verb qq has another feature ...
Verb qq can be called dyadically too. Give it a left argument x if you want to verify you really do know how to code the value of y .
If x-:y then the diagnostic line starts with '=', else it starts with '~' :
a: qq i.0 0 NB. No, that's not how Ace is defined ┌───────────────┐ │~integer/$y:0 0│ ├───────────────┤ └───────────────┘ a: qq <i.0 NB. x y match, like you'd expect ┌──────────┐ │=boxed/$y:│ ├──────────┤ │┌┐ │ │││ │ │└┘ │ └──────────┘ a: qq <'' NB. x y match, but maybe you wouldn't expect ┌──────────┐ │=boxed/$y:│ ├──────────┤ │┌┐ │ │││ │ │└┘ │ └──────────┘ a: qq <' ' NB. x y don't match ┌──────────┐ │~boxed/$y:│ ├──────────┤ │┌─┐ │ ││ │ │ │└─┘ │ └──────────┘ a: qq <,' ' NB. -nor here ┌──────────┐ │~boxed/$y:│ ├──────────┤ │┌─┐ │ ││ │ │ │└─┘ │ └──────────┘ (<' ') qq <,' ' NB. and those two y's don't match either ┌──────────┐ │~boxed/$y:│ ├──────────┤ │┌─┐ │ ││ │ │ │└─┘ │ └──────────┘
Exercises for the reader
Try qq with empty vectors obtained from various boxed / unboxed lists, eg:
'' qq 0 {. 'abc' ; 'def' ; 'ghi'
'' qq 0 $ 123 456 789
'' qq 0 $ 123 ; 456 ; 789
(Contributed by IanClark.)
