Popular Elephant Traps #1: Scalars, empty vectors, and vectors of length one

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.

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.

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 ...

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'
abcde

Yet 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.)

IanClark/InspectingValues (last edited 2011-04-08 16:35:37 by IanClark)