Author Topic: Visual LISP: Understanding Variants and Safearrays  (Read 15757 times)

0 Members and 1 Guest are viewing this topic.

nivuahc

  • Guest
Visual LISP: Understanding Variants and Safearrays
« on: January 14, 2010, 08:46:07 AM »
I'm trying to improve my abilities with Visual LISP and I'm having a bit of trouble understanding Variants and Safearrays. I've read and re-read Chapter 6 in the Visual LISP Developer's Bible, which covers those topics, but I'm just not seeing it.

I'm a visual learner (no pun intended) and I can generally see, in my head, a visual representation of the code that I'm writing. For example, I see (cadr MyList) physically grabbing the second element of a MyList (like cadr is physically reaching into MyList and pulling the second element out). I know that might sound silly to some but that's how I learn and retain information.

So I'm not quite getting Variants and Safearrays because I'm having trouble visualizing them. I'm wondering if any of the local Swamp Whiz Kids can shed a little light on the subject, in a way that's easy to understand (visually, for a guy like me) because when I read about them I can't help but think to myself "I could probably utilize this if I understood how it worked".

Any takers?

Joe Burke

  • Guest
Re: Visual LISP: Understanding Variants and Safearrays
« Reply #1 on: January 14, 2010, 09:48:08 AM »
Just my two cents...

Variants and safearrrays are not things which you typically have to deal while using vlisp calls. Particularly if you use these vlisp functions, vlax-get, vlax-put and vlax-invoke.

For instance:

Command: (vlax-get-property <pline objet> 'coordinates)
Select vla object: #<variant 8197 ...>

Whereas (vlax-get <pline objet> 'coordinates)
Retuns a flat list of coordinates.

IOW there's quite bit of confusing documention floating around.

CAB

  • Global Moderator
  • Seagull
  • Posts: 10369
Re: Visual LISP: Understanding Variants and Safearrays
« Reply #2 on: January 14, 2010, 09:52:00 AM »
Safearray's are a PITA. 8-)

Have you read this http://www.afralisp.net/vl/array.htm
You may have to "Select ALL" to see the text, bad color choice.

Here is one example that I liked to explain how the list are arranged. [that was confusing to me]
Code: [Select]
;; The inner list is the LAST array dim '(0 . 3) = 4 ""
;;  while '(0 . 2) = 3 lists
(vlax-safearray->list (vlax-make-safearray vlax-vbString '(0 . 2) '(0 . 3)))
(("" "" "" "") ("" "" "" "") ("" "" "" ""))

(vlax-safearray->list (vlax-make-safearray vlax-vbString '(0 . 3) '(0 . 2)))
(("" "" "") ("" "" "") ("" "" "") ("" "" ""))


IMO dealing with list are much more straight forward.

I've reached the age where the happy hour is a nap. ()
Windows 10 core i7 4790k 4Ghz 32GB GTX 970
Please support this web site.

gile

  • Water Moccasin
  • Posts: 2233
  • Marseille, France
Re: Visual LISP: Understanding Variants and Safearrays
« Reply #3 on: January 14, 2010, 10:29:41 AM »
Hi,

I agree with Joe and Alan.
LISP is LISt Processing, variants and safearrays are COM/ActiveX data types.

So I avoid dealing with variants and safearrays with LISP because it's not as easy and powerfull than dealing with lists.

Some times, the vlax-get, vlax-put or vlax-invoke can't be used, so rather than using the variants, I convert them from or into lists with this little LISP library.

Code: [Select]
;;;======================== VARIANTS & SAFEARRAYS ========================;;;

;; Variant -> LISP

;; gc:VariantToLispData
;; Converts a variant or a safearray into LISP data (list)
;;
;; Argument: var variant or safearray

(defun gc:VariantToLispData (var)
  (cond
    ((= (type var) 'variant)
     (gc:VariantToLispData (vlax-variant-value var)))
    ((= (type var) 'safearray)
     (mapcar 'gc:VariantToLispData (vlax-safearray->list var))
    )
    (T var)
  )
)

;; gc:2dVariantToPointList
;; Converts a variant of 2D coordinates into a 2d points list
;; LightweightPolyline: OCS coordinates
;;
;; Argument
;; var: a variant (array of doubles) as returned by vla-get-Coordinates

(defun gc:2dVariantToPointList (var / foo)
  (defun foo (lst)
    (if lst
      (cons (list (car lst) (cadr lst)) (foo (cddr lst)))
    )
  )
  (foo (vlax-safearray->list (vlax-variant-value var)))
)

;; gc:3dVariantToPointList
;; Converts a variant of 3D coordinates into a 3d points list
;; 2d Polyline: OCS coordinates (Z = 0)
;; 3DFace, 3DPolyline, Leader, MLine, PolyfaceMesh,
;; PolygonMesh, Solid, Trace: WCS coordinates
;;
;; Argument
;; var: a variant (array of doubles) as returned by vla-get-Coordinates

(defun gc:3dVariantToPointList (var / foo)
  (defun foo (lst)
    (if lst
      (cons (list (car lst) (cadr lst) (caddr lst)) (foo (cdddr lst)))
    )
  )
  (foo (vlax-safearray->list (vlax-variant-value var)))
)

;; gc:VariantsToDxfList
;; Returns an assoc list (DXF list type)
;;
;; Arguments
;; xtyp: variant (array of integers)
;; xval: varinat (array of variants)

(defun gc:VariantsToDxfList (xtyp xval)
  (mapcar 'cons (gc:VariantToLispData xtyp) (gc:VariantToLispData xval))
)

;; gc:GetXdata
;; Returns the object xadta list
;;
;; Arguments
;; obj: (vla-object) the object containing xdata
;; app: (string) the registred application name ("" for all)

(defun gc:GetXdata (obj app / xtyp xval)
  (vla-GetXdata obj app 'xtyp 'xval)
  (gc:VariantsToDxfList xtyp xval)
)

;; gc:GetXrecordData
;; Returns the xrecord object DXF data list
;;
;; Arguments
;; xrec: (vla-object) thet XRECORD object

(defun gc:GetXrecordData (xrec / xtyp xval)
  (vla-GetXrecordData xrec 'xtyp 'xval)
  (gc:VariantsToDxfList xtyp xval)
)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; LISP -> variant

;; gc:2dPointListToVariant (gile)
;; Return a variant of 2d coordinates
;;
;; Argument: a 2d points list -type (x y)-

(defun gc:2dPointListToVariant (lst)
  (vlax-make-variant
    (vlax-safearray-fill
      (vlax-make-safearray
        vlax-VbDouble
        (cons 0 (1- (* 2 (length lst))))
      )
      (apply 'append lst)
    )
  )
)

;; gc:3dPointListToVariant (gile)
;; Return a variant of 3d coordinates
;;
;; Argument: a 3d points list -type (x y z)-

(defun gc:3dPointListToVariant (lst)
  (vlax-make-variant
    (vlax-safearray-fill
      (vlax-make-safearray
        vlax-VbDouble
        (cons 0 (1- (* 3 (length lst))))
      )
      (apply 'append lst)
    )
  )
)

;; gc:ObjectListToVariant
;; returns a variant (array of objects)
;;
;; Argument
;; lst: a vla-object list

(defun gc:ObjectListToVariant (lst)
  (vlax-make-variant
    (vlax-safearray-fill
      (vlax-make-safearray
        vlax-vbObject
        (cons 0 (1- (length lst)))
      )
      lst
    )
  )
)

;; gc:DxfListToVariants
;; Defines 2 variables and bounds a variant to each
;;
;; Arguments
;; lst: a DXF list
;; typeSymbol: a quoted symbol (other than 'typeSymbol)
;; valueSymbol: a quoted symbol (other than 'valueSymbol)

(defun gc:DxfListToVariants (lst typeSymbol valueSymbol)
  (set typeSymbol
       (vlax-make-variant
         (vlax-safearray-fill
           (vlax-make-safearray
             vlax-vbInteger
             (cons 0 (1- (length lst)))
           )
           (mapcar 'car lst)
         )
       )
  )
  (set valueSymbol
       (vlax-make-variant
         (vlax-safearray-fill
           (vlax-make-safearray
             vlax-vbVariant
             (cons 0 (1- (length lst)))
           )
           (mapcar '(lambda (x)
                      (if (listp (setq x (cdr x)))
                        (vlax-3d-point x)
                        (vlax-make-variant x)
                      )
                    )
                   lst
           )
         )
       )
  )
)


;; gc:SetXdata
;; Set xdatas to an object
;;
;; Arguments
;; obj: (vla-object) the object to set xdatas
;; lst: (liste DXF) the xdatas as:
;; '((1001 . "App_Name") (1002 . "{") (1000 . "string") (1070 . 1) (1002 . "}"))

(defun gc:SetXdata (obj lst / xtyp xval)
  (gc:DxfListToVariants lst 'xtyp 'xval)
  (vla-SetXdata obj xtyp xval)
)

;; gc:SetXrecordData
;; Set datas to an xrecord
;;
;; Arguments
;; xrec: (vla-object) the Xrecord object
;; lst : (liste DXF) the datas as:
;; '((1 . "string") (70 . 1) (10 1.0 2.0 0.0))

(defun gc:SetXrecordData (xrec lst / xtyp xval)
  (gc:DxfListToVariants lst 'xtyp 'xval)
  (vla-SetXrecordData xrec xtyp xval)
)

Using examples:
(gc:SetXdata (vlax-ename->vla-object (car (entsel)))
             '((1001 . "MyApp") (1000 . "This is a test"))
)

(gc:GetXdata (vlax-ename->vla-object (car (entsel))) "MyApp")
Speaking English as a French Frog

nivuahc

  • Guest
Re: Visual LISP: Understanding Variants and Safearrays
« Reply #4 on: January 14, 2010, 11:17:27 AM »
Just my two cents...

Variants and safearrrays are not things which you typically have to deal while using vlisp calls. Particularly if you use these vlisp functions, vlax-get, vlax-put and vlax-invoke.

For instance:

Command: (vlax-get-property <pline objet> 'coordinates)
Select vla object: #<variant 8197 ...>

Whereas (vlax-get <pline objet> 'coordinates)
Retuns a flat list of coordinates.

IOW there's quite bit of confusing documention floating around.

In my reading of Variants and Safearrays I got to thinking about the problem I was having here. Not that I could use a Safearray to solve the issue in a better way but... I guess it got me thinking about Lists in general, and how I typically build/parse them.

My reasons for wanting to improve my abilites with Visual LISP is because I'm trying to make my work cleaner, easier to understand, and easier for me to process after-the-fact. I've got little utility routines that I'd written years ago for replacing characters in a string, for example. I'm a poor programmer on my best days and one of my major faults is that I either over-comment the things that I write or I put no comments at all. Some of these routines, in which I hadn't commented a bit of it, I find myself working through the routine step by step, trying to visualize the process in my head, and invariably getting confused along the way and having to start over. I guess it's just me getting old.


Safearray's are a PITA. 8-)

Have you read this http://www.afralisp.net/vl/array.htm
You may have to "Select ALL" to see the text, bad color choice.

Here is one example that I liked to explain how the list are arranged. [that was confusing to me]
Code: [Select]
;; The inner list is the LAST array dim '(0 . 3) = 4 ""
;;  while '(0 . 2) = 3 lists
(vlax-safearray->list (vlax-make-safearray vlax-vbString '(0 . 2) '(0 . 3)))
(("" "" "" "") ("" "" "" "") ("" "" "" ""))

(vlax-safearray->list (vlax-make-safearray vlax-vbString '(0 . 3) '(0 . 2)))
(("" "" "") ("" "" "") ("" "" "") ("" "" ""))


IMO dealing with list are much more straight forward.


I read that article that Kenny wrote and I have a better understanding of Safearrays now, than before I read it, but there are still bits and pieces that I find terribly confusing. For example:

Quote
The remaining arguments to "vlax-make-safearray" specify the upper and lower bounds of each dimension of the array. The lower bound for an index can be zero or any positive or negative number. Have another look at the function we called earlier :

Code: [Select]
_$ (setq sheet_type (vlax-make-safearray vlax-vbString '(0 . 2)))
This function created a single-dimension array consisting of three strings with a starting index of 0 (element 0, element 1 and element 2).
Consider this :

Code: [Select]
_$ (setq pt1 (vlax-make-safearray vlax-vbDouble '(1 . 3)))
The lower bound specified in this example is one and the upper bound specified is three, so the array will hold three doubles (element 1, element 2 and element 3).

And later...

Quote
Let's create a Array with two dimensions, each dimension with three elements:

Code: [Select]
_$ (setq two_dim (vlax-make-safearray vlax-vbString '(0 . 1) '(1 . 3)))
#<safearray...>

_$ (vlax-safearray-fill two_dim '(("Sheet1" "Sheet2" "Sheet3") ("a" "b" "c")))
#<safearray...>

_$ (vlax-safearray->list two_dim)
(("Sheet1" "Sheet2" "Sheet3") ("a" "b" "c"))

This is just a list of lists.
The first list, '(0 . 1) is the number of dimensions.
The second list, '(1 . 3) is the number of elements

And now a three dimensional Array with two elements in each dimension:

Code: [Select]
_$ (setq three_dim (vlax-make-safearray vlax-vbString '(0 . 2) '(1 . 2)))
#<safearray...>

_$ (vlax-safearray-fill three_dim '(("Sheet1" "Sheet2") ("a" "b") ("d" "e")))
#<safearray...>

_$ (vlax-safearray->list three_dim)
(("Sheet1" "Sheet2") ("a" "b") ("d" "e"))

Here we have a list of three lists.
This time, the first list '(0 . 2) defines three dimensions and the second '(1 . 2) defines 2 elements in each dimension.

Let's see if I'm on the right track here...

Code: [Select]
(setq two_dim (vlax-make-safearray vlax-vbString '(0 . 1) '(1 . 3)))
(setq three_dim (vlax-make-safearray vlax-vbString '(0 . 2) '(1 . 2)))

Quote
The remaining arguments to "vlax-make-safearray" specify the upper and lower bounds of each dimension of the array. The lower bound for an index can be zero or any positive or negative number.

So the arguments to vlax-make-safearray are, first, the Data Type (in these examples vlax-vbString) and, next, the number of dimensions (or lists, counting from 0) and the number of elements in each list (counting from 1)?

So, in the following:

Quote
Have a look at the following :

_$ (setq pt1 (vlax-make-safearray vlax-vbDouble '(1 . 3)))
#<safearray...>

_$ (vlax-safearray-put-element pt1 1 100)
100

_$ (vlax-safearray-put-element pt1 2 100)
100

_$ (vlax-safearray-put-element pt1 3 75)
75

_$ (vlax-safearray->list pt1)
(100.0 100.0 75.0)

_$ (vlax-safearray-put-element pt1 1 50)
50

_$ (vlax-safearray->list pt1)
(50.0 100.0 75.0)

There's only one dimension that we're dealing with here so we specify that the dimension has 3 elements in it. Yeah?

Quote
Now let's populate a two-dimension array of strings :

_$ (setq two_dim (vlax-make-safearray vlax-vbString '(0 . 1) '(1 . 3)))
#<safearray...>

_$ (vlax-safearray-put-element two_dim 0 1 "a")
"a"

_$ (vlax-safearray->list two_dim)
(("a" "" "") ("" "" ""))

_$ (vlax-safearray-put-element two_dim 0 2 "b")
"b"

_$ (vlax-safearray-put-element two_dim 0 3 "c")
"c"

_$ (vlax-safearray-put-element two_dim 1 1 "d")
"d"

_$ (vlax-safearray-put-element two_dim 1 2 "e")
"e"

_$ (vlax-safearray-put-element two_dim 1 3 "f")
"f"

_$ (vlax-safearray->list two_dim)
(("a" "b" "c") ("d" "e" "f"))

'(0 . 1) = 2 lists (0, & 1)
'(1 . 3) = 3 elements per list (1, 2, & 3)

That about right?

Could that have been written this way as well?

Code: [Select]
(setq two_dim (vlax-make-safearray vlax-vbString '(1 . 2) '(1 . 3)))
'(1 . 2) = 2 lists (1, & 2)
'(1 . 3) = 3 elements per list (1, 2, & 3)

Or like this?

Code: [Select]
(setq two_dim (vlax-make-safearray vlax-vbString '(1 . 2) '(0 . 2)))
'(1 . 2) = 2 lists (1, & 2)
'(0 . 2) = 3 elements per list (0, 1, & 2)

nivuahc

  • Guest
Re: Visual LISP: Understanding Variants and Safearrays
« Reply #5 on: January 14, 2010, 11:29:05 AM »
Hi,

I agree with Joe and Alan.
LISP is LISt Processing, variants and safearrays are COM/ActiveX data types.

So I avoid dealing with variants and safearrays with LISP because it's not as easy and powerfull than dealing with lists.

Some times, the vlax-get, vlax-put or vlax-invoke can't be used, so rather than using the variants, I convert them from or into lists with this little LISP library.

Code: [Select]
;;;======================== VARIANTS & SAFEARRAYS ========================;;;

;; Variant -> LISP

;; gc:VariantToLispData
;; Converts a variant or a safearray into LISP data (list)
;;
;; Argument: var variant or safearray

(defun gc:VariantToLispData (var)
  (cond
    ((= (type var) 'variant)
     (gc:VariantToLispData (vlax-variant-value var)))
    ((= (type var) 'safearray)
     (mapcar 'gc:VariantToLispData (vlax-safearray->list var))
    )
    (T var)
  )
)

;; gc:2dVariantToPointList
;; Converts a variant of 2D coordinates into a 2d points list
;; LightweightPolyline: OCS coordinates
;;
;; Argument
;; var: a variant (array of doubles) as returned by vla-get-Coordinates

(defun gc:2dVariantToPointList (var / foo)
  (defun foo (lst)
    (if lst
      (cons (list (car lst) (cadr lst)) (foo (cddr lst)))
    )
  )
  (foo (vlax-safearray->list (vlax-variant-value var)))
)

;; gc:3dVariantToPointList
;; Converts a variant of 3D coordinates into a 3d points list
;; 2d Polyline: OCS coordinates (Z = 0)
;; 3DFace, 3DPolyline, Leader, MLine, PolyfaceMesh,
;; PolygonMesh, Solid, Trace: WCS coordinates
;;
;; Argument
;; var: a variant (array of doubles) as returned by vla-get-Coordinates

(defun gc:3dVariantToPointList (var / foo)
  (defun foo (lst)
    (if lst
      (cons (list (car lst) (cadr lst) (caddr lst)) (foo (cdddr lst)))
    )
  )
  (foo (vlax-safearray->list (vlax-variant-value var)))
)

;; gc:VariantsToDxfList
;; Returns an assoc list (DXF list type)
;;
;; Arguments
;; xtyp: variant (array of integers)
;; xval: varinat (array of variants)

(defun gc:VariantsToDxfList (xtyp xval)
  (mapcar 'cons (gc:VariantToLispData xtyp) (gc:VariantToLispData xval))
)

;; gc:GetXdata
;; Returns the object xadta list
;;
;; Arguments
;; obj: (vla-object) the object containing xdata
;; app: (string) the registred application name ("" for all)

(defun gc:GetXdata (obj app / xtyp xval)
  (vla-GetXdata obj app 'xtyp 'xval)
  (gc:VariantsToDxfList xtyp xval)
)

;; gc:GetXrecordData
;; Returns the xrecord object DXF data list
;;
;; Arguments
;; xrec: (vla-object) thet XRECORD object

(defun gc:GetXrecordData (xrec / xtyp xval)
  (vla-GetXrecordData xrec 'xtyp 'xval)
  (gc:VariantsToDxfList xtyp xval)
)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; LISP -> variant

;; gc:2dPointListToVariant (gile)
;; Return a variant of 2d coordinates
;;
;; Argument: a 2d points list -type (x y)-

(defun gc:2dPointListToVariant (lst)
  (vlax-make-variant
    (vlax-safearray-fill
      (vlax-make-safearray
        vlax-VbDouble
        (cons 0 (1- (* 2 (length lst))))
      )
      (apply 'append lst)
    )
  )
)

;; gc:3dPointListToVariant (gile)
;; Return a variant of 3d coordinates
;;
;; Argument: a 3d points list -type (x y z)-

(defun gc:3dPointListToVariant (lst)
  (vlax-make-variant
    (vlax-safearray-fill
      (vlax-make-safearray
        vlax-VbDouble
        (cons 0 (1- (* 3 (length lst))))
      )
      (apply 'append lst)
    )
  )
)

;; gc:ObjectListToVariant
;; returns a variant (array of objects)
;;
;; Argument
;; lst: a vla-object list

(defun gc:ObjectListToVariant (lst)
  (vlax-make-variant
    (vlax-safearray-fill
      (vlax-make-safearray
        vlax-vbObject
        (cons 0 (1- (length lst)))
      )
      lst
    )
  )
)

;; gc:DxfListToVariants
;; Defines 2 variables and bounds a variant to each
;;
;; Arguments
;; lst: a DXF list
;; typeSymbol: a quoted symbol (other than 'typeSymbol)
;; valueSymbol: a quoted symbol (other than 'valueSymbol)

(defun gc:DxfListToVariants (lst typeSymbol valueSymbol)
  (set typeSymbol
       (vlax-make-variant
         (vlax-safearray-fill
           (vlax-make-safearray
             vlax-vbInteger
             (cons 0 (1- (length lst)))
           )
           (mapcar 'car lst)
         )
       )
  )
  (set valueSymbol
       (vlax-make-variant
         (vlax-safearray-fill
           (vlax-make-safearray
             vlax-vbVariant
             (cons 0 (1- (length lst)))
           )
           (mapcar '(lambda (x)
                      (if (listp (setq x (cdr x)))
                        (vlax-3d-point x)
                        (vlax-make-variant x)
                      )
                    )
                   lst
           )
         )
       )
  )
)


;; gc:SetXdata
;; Set xdatas to an object
;;
;; Arguments
;; obj: (vla-object) the object to set xdatas
;; lst: (liste DXF) the xdatas as:
;; '((1001 . "App_Name") (1002 . "{") (1000 . "string") (1070 . 1) (1002 . "}"))

(defun gc:SetXdata (obj lst / xtyp xval)
  (gc:DxfListToVariants lst 'xtyp 'xval)
  (vla-SetXdata obj xtyp xval)
)

;; gc:SetXrecordData
;; Set datas to an xrecord
;;
;; Arguments
;; xrec: (vla-object) the Xrecord object
;; lst : (liste DXF) the datas as:
;; '((1 . "string") (70 . 1) (10 1.0 2.0 0.0))

(defun gc:SetXrecordData (xrec lst / xtyp xval)
  (gc:DxfListToVariants lst 'xtyp 'xval)
  (vla-SetXrecordData xrec xtyp xval)
)

Using examples:
(gc:SetXdata (vlax-ename->vla-object (car (entsel)))
             '((1001 . "MyApp") (1000 . "This is a test"))
)

(gc:GetXdata (vlax-ename->vla-object (car (entsel))) "MyApp")

Gile,

That's some handy stuff right there  :-)

nivuahc

  • Guest
Re: Visual LISP: Understanding Variants and Safearrays
« Reply #6 on: January 14, 2010, 12:29:40 PM »
Okay, after pondering this a bit more how does this sound?

A single dimension Safearray is a list.

A multi-dimension Safearray is a list of lists.

A Safearray is static, meaning it has however many elements in each list as specified, no more, no less.

That sound about right?

gile

  • Water Moccasin
  • Posts: 2233
  • Marseille, France
Re: Visual LISP: Understanding Variants and Safearrays
« Reply #7 on: January 14, 2010, 01:09:41 PM »
Quote
A single dimension Safearray is a list.

A multi-dimension Safearray is a list of lists.
That's the way you can see it through LISP.

In other laguages arrays are compared to tables: a single dimension array is like a single row table, a 2 dimension array '(0 . 3) '(0 .4) is like a table with 5 rows and 4 columns...

Its number of cells is static, and all datas it contains have to be the same type. Most laguages using the COM interop are static typed (VB(A), VB.net, C#, etc.), this is the main difference with LISP which is dynamic types (a list can contain different type datas).
This is the reason why some safearrays have to be arrays of variants. A variant is a container for one data (this data can be any type as string integer double object or safearray).
This way, it's possible to build arrays as those requiered by the vla-SetXdata for example.
These array are quite equivalent to a LISP dotted pair list (as a DXF list) :
- one array (data type) is an array of integers  (the 'car' of the pairs)
- the other array (data values) have to be a variant array because the values (the 'cdr' of the pairs) can be different types.

Excpect it's not too confusing (my English is quite poor).
Speaking English as a French Frog

nivuahc

  • Guest
Re: Visual LISP: Understanding Variants and Safearrays
« Reply #8 on: January 14, 2010, 01:21:48 PM »
Quote
A single dimension Safearray is a list.

A multi-dimension Safearray is a list of lists.
That's the way you can see it through LISP.

In other laguages arrays are compared to tables: a single dimension array is like a single row table, a 2 dimension array '(0 . 3) '(0 .4) is like a table with 5 rows and 4 columns...

Its number of cells is static, and all datas it contains have to be the same type. Most laguages using the COM interop are static typed (VB(A), VB.net, C#, etc.), this is the main difference with LISP which is dynamic types (a list can contain different type datas).
This is the reason why some safearrays have to be arrays of variants. A variant is a container for one data (this data can be any type as string integer double object or safearray).
This way, it's possible to build arrays as those requiered by the vla-SetXdata for example.
These array are quite equivalent to a LISP dotted pair list (as a DXF list) :
- one array (data type) is an array of integers  (the 'car' of the pairs)
- the other array (data values) have to be a variant array because the values (the 'cdr' of the pairs) can be different types.

Excpect it's not too confusing (my English is quite poor).

Actually, Gile, your English was just fine. You explained that pretty well. Thanks! :)


Lee Mac

  • Seagull
  • Posts: 12285
  • London, England
Re: Visual LISP: Understanding Variants and Safearrays
« Reply #9 on: January 14, 2010, 01:48:19 PM »
Nice explanation Gile - that helped to reinforce my knowledge of the subject  ;-)

SteveK

  • Guest
Re: Visual LISP: Understanding Variants and Safearrays
« Reply #10 on: January 14, 2010, 04:11:05 PM »
and thanks for the lisp library Gile.

nivuahc

  • Guest
Re: Visual LISP: Understanding Variants and Safearrays
« Reply #11 on: January 17, 2010, 11:36:16 AM »
Following up on the explanation by Gile, and after pondering all of this for a bit, this is where I'm at regarding Saferrays:

Thinking about arrays as a table:



Thinking of this as lists: The dotted pairs (dimensions) describe the elements contained in the previous list. If there is no previous list (a single dimension array), the dimension describes the number of elements in a single list.

The first number in a dimension (lower index boundary) indicates the number we start counting from and the second number (upper index boundary) indicates the number where we stop our counting. In other words, the array above could have easily been written as such:

Code: [Select]
(vlax-make-safearray vlax-vbString '(1 . 2) '(2 . 3) '(0 . 1))
A list, containing 2 lists, each containing two lists, each containing 2 empty strings, or

Code: [Select]
((("" "") ("" "")) (("" "") ("" "")))
You can have a maximum of 16 dimensions in an array. So, while the following would work:

Code: [Select]
(vlax-make-safearray vlax-vbString '(1 . 2) '(1 . 2) '(1 . 2) '(1 . 2) '(1 . 2) '(1 . 2) '(1 . 2) '(1 . 2) '(1 . 2) '(1 . 2) '(1 . 2) '(1 . 2) '(1 . 2) '(1 . 2) '(1 . 2) '(1 . 2))
Adding one more dimension would not:

Code: [Select]
(vlax-make-safearray vlax-vbString '(1 . 2) '(1 . 2) '(1 . 2) '(1 . 2) '(1 . 2) '(1 . 2) '(1 . 2) '(1 . 2) '(1 . 2) '(1 . 2) '(1 . 2) '(1 . 2) '(1 . 2) '(1 . 2) '(1 . 2) '(1 . 2) '(1 . 2))
The first two dimensions would be processed and the rest would be ignored. I've tested this with a variety of numbers past 16 and it always processes the first two dimensions and ignores the rest. Oddly enough, it doesn't throw an error out at you.

That sound about right?

Lee Mac

  • Seagull
  • Posts: 12285
  • London, England
Re: Visual LISP: Understanding Variants and Safearrays
« Reply #12 on: January 17, 2010, 04:35:55 PM »
That sounds like a good explanation - I thought about it in a similar way here, but you had a better diagram  :-D

One last question from me:  Is there any significance in the numbers used in the safearray upper and lower dimension bounds? (Apart from the difference between them of course)

nivuahc

  • Guest
Re: Visual LISP: Understanding Variants and Safearrays
« Reply #13 on: January 17, 2010, 04:48:32 PM »
That sounds like a good explanation - I thought about it in a similar way here, but you had a better diagram  :-D

One last question from me:  Is there any significance in the numbers used in the safearray upper and lower dimension bounds? (Apart from the difference between them of course)

Not that I've been able to find, though I have noticed a propensity for people to do things like this:

Code: [Select]
(vlax-make-safearray vlax-vbString '([color=red]0[/color] . 4) '([color=red]1[/color] . 3) '([color=red]2[/color] . 6))
and I've wondered if keeping the lower index boundary numbers different makes things simpler somehow... or if it was a necessity of some sort that I'm unable to determine. In my mind, making them all the same makes digging through them later a bit easier. But it's fairly likely that I'm missing something.

Lee Mac

  • Seagull
  • Posts: 12285
  • London, England
Re: Visual LISP: Understanding Variants and Safearrays
« Reply #14 on: January 17, 2010, 04:55:26 PM »
Thats interesting that you should say that - I wondered whether the numbers should be the other way around, as I described in this post in the thread, the dimensions seem to run from the right to the left, but then I'm not entirely sure that the innermost element is the "first" dimension, or the "last"...

Perhaps I don't understand as much as I think I do...