=head1 NAME Inline::SLang::Array - Support for arrays =head1 SYNOPSIS use Inline 'SLang'; # you can send arrays to S-Lang print_in_slang( [ 1, 2, 4 ] ); # and get them back from S-Lang $aref = get_from_slang(); print "The array contains: " . join(',', @$aref) . "\n"; __END__ __SLang__ define print_in_slang (arr) { variable adims, ndim, atype; ( adims, ndim, atype ) = array_info(arr); vmessage( "Array has type=%S with %d dims", atype, ndim ); foreach ( arr ) { variable val = (); vmessage( " Value = %s", string(val) ); } } define get_from_slang() { return ["a string","another one","3"]; } The output of this code - which can be found in the source-code distribution as F - is: Array has type=Integer_Type with 1 dims Value = 1 Value = 2 Value = 4 The array contains: a string,another one,3 =head1 DESCRIPTION The philosophy behind arrays in Perl and S-Lang is somewhat different, which makes converting arrays between the two languages a bit more awkward than with many of the other data types. We admit three different array representations in Perl: Perl's array references; as an C object (this class is a part of the C package); and as a piddle - the fundamental object of the L - which is the suggested method to analyze large numeric arrays in Perl. Before discussing this I want to talk about going the "other way". =head2 Things to remember when converting arrays to S-Lang Consider the following Perl array reference: [ [ 2, 1 ], [ 4, 5 ], [ 8, 9 ] ] This could be represented in S-Lang as one of the following arrays: Integer_Type [3,2] Double_Type [3,2] String_Type [3,2] Array_Type [3] Any_Type [3] Note that this does B match the way that PDL represents array (which considers this to be a C<2x3> array). As warned in earlier versions this is a problem now that piddles are supported. Please see the L<"Issues with the PDL support"> section below. Admittedly most of these are unlikely, but the point is that they are all I representations, which makes automatically converting such an item to S-Lang awkward. As discussed below it is possible to provide extra information (the type and/or dimensionality of the array) to help the conversion - using an C object or the C function - but hopefully the guesses the code makes will be sufficient. =head3 How do we guess the array? If the array is stored as a I or an C object then the computer can find out the information without guessing, so the following is only relevant when an I is used. When given an array reference, the code looks at the first element to decide what type of array it is: if it is an array reference then we have a multi-dimensional array and the process is repeated until we reach a value. So, in the example above the "2" would be used to determine the type of the array - so it should come out as C - and the array dimensions are taken to be C<[3,2]>. =head4 How to confuse the array conversion (by data type) Since the code only looks at the first element in the array to guess the type it is not too hard to come across arrays that will cause the code to croak with some bizarre message from S-Lang. For instance: [ 1, 2, "a string" ] will cause a problem since it is assumed to be an C array, which is fine for the first two elements but not for the last one. The code could be updated so that it checks all elements in the array and finds the most suitable type and dimensionality; however, this could be a large performance hit for large arrays and the current advice is to use one of the techniques we describe below. =head4 How to confuse the array conversion (by array size) The code assumes that the array dimensions it calculates hold for all elements. If they don't then you will see error messages (quite possibly strange ones at that). As an example, [ [ 2, 1 ], [1] ] is guaranteed not to be liked by the code; on my machine it dislikes it so much that it causes a B. =head3 How can I help? If you know the array size and/or type then you can use the C function to help the code out. As an example (see also F): use Inline 'SLang' => Config => EXPORT => [ 'sl_array', 'Integer_Type' ]; use Inline 'SLang'; my $aref = [ 1, 3, 2 ]; foo( $aref ); foo( sl_array( $aref, "Int_Type" ) ); foo( sl_array( $aref, [3] ) ); foo( sl_array( $aref, [3], Integer_Type() ) ); __END__ __SLang__ define foo(x) { vmessage("Array has type: %S", _typeof(x) ); } which produces four lines saying Array has type: Integer_Type Note that I snuck in an example of using an automatically-generated wrapper around the C constructor - the C function in the last call to C. Unfortunately you can I use C to typecast the array: at least not at the moment. If you replace C with C in the above then you will come across the following error message S-Lang Error: Type Mismatch: Unable to typecast Integer_Type to String_Type even though Perl can happily treat 1 as an integer or a string. Suggestions on how to handle this case are welcome. The C function is actually a wrapper around the constructor call for the C class which tries to guess the array type and size if they have not been supplied. =head2 Things to remember when converting arrays to Perl There are three different ways that a S-Lang array can be represented in Perl: =over 4 =item 1 As a reference to a Perl array. =item 2 As a piddle (see the L documentation) for numeric types. Please see the L<"Issues with the PDL support"> section below. =item 3 As an C object. =back In most cases the first two representations are likely to be the ones you use; the use of an C object is only needed when it is I for the code to guess the type/dimensionality of an array. The C function is used to define how S-Lang arrays are converted to Perl, and to find out what the current conversion system is. This function is described in L. =head3 Example use Inline 'SLang' => Config => EXPORT => [ 'sl_array2perl' ]; use Inline 'SLang'; # use array references for all S-Lang arrays sl_array2perl(0); my $a0 = get_array(); print "Array returned as an " . ref($a0) . "\n"; print " dim size = " . (1+$#$a0) . "\n"; # use Array_Type for all S-Lang arrays sl_array2perl(1); my $a1 = get_array(); print "Array returned as an " . ref($a1) . "\n"; my ( $dims, $ndim, $atype ) = $a1->array_info(); print " dim size = $$dims[0]\n"; print " type = $atype\n"; # use a piddle (assumes Inline::SLang::sl_have_pdl() == 1) sl_array2perl(2); my $a2 = get_array(); print "Array returned as an " . ref($a1) . "\n"; print " dim size = " . $a2->getdims(0) . "\n"; print " type = " . $a2->type . "\n"; __END__ __SLang__ define get_array() { return [ 14.0, 3 ]; } This example is available as F in the source distribution. When run this outputs: Array returned as an ARRAY dim size = 2 Array returned as an Array_Type dim size = 2 type = Double_Type B: add the output from the array-as-piddle part of the code. =head1 Issues with the PDL support Version B<0.20> of C adds support for the L. Unfortunately this support is still a work in progress. =over 2 =item * 1D piddles and S-Lang arrays should work properly. =item * Conversion of > 1D arrays/piddles is I because what to S-Lang is a 2x3 array is, to PDL, a 3x2 array! Currently the arrays are converted like this (ie the order of the arrays is swapped). I do B think this a good long-term solution. Have I any ideas? =item * 0-dimension piddles are not converted to S-Lang, since S-Lang does not allow such an array. We could convert the piddle to a scalar or a single-element array. =item * It has only been tested on 32-bit machines. =back =head1 THE ARRAY_TYPE CLASS The Perl C class stores the datatype and dimensionality of an array along with the data. In most cases this information is not needed so you should stick with using array references (or I) since it's much easier, but there are times when it can come in handy. The C class inherit the default methods of all the Inline::SLang objects, namely: =over 2 =item * C This returns C as a C object. See the C<_typeof()> method below to find the datatype used to store the data. =item * C This returns the string "Array_Type". =item * C This returns 0. =back As with the other object classes these methods can only be called using the C<< $object->method( args ) >> syntax. There are also a number of additional methods for this class: =over 2 =item * C The constructor takes the datatype of the array - as either a string or a C object - and an array reference containing the size of the array and returns an C object. If a third argument is supplied then it is taken to be an array reference containing the data to be stored in the array. Note that in this case we just copy the array reference and B the actual data stored in this array. This means that you can change the data stored in the object: useful but can cause bizarre behaviour for the unwary. my $arr1 = Array_Type->new( "Int_Type", [3,2] ); my $arr2 = Array_Type->new( "String_Type", [3], [ "a string", "another string", "boo" ] ); There is B sanity checking of the input values; if they do not agree then something strange will happen at some point during the execution of the progam. An alternative way to create an C object is to use the C function, which is described in the L document. This has the advantage that it will try and guess the type and/or dimensionality of the array if you do not specify it. =item * C<_typeof()> Returns - as a C object - the class of the data stored in the array. my $arr1 = Array_Type->new( "Int_Type", [3,2] ); my $type = $arr1->_typeof(); =item * C Mimics the S-Lang C function (slightly more efficient than usng the S-Lang version). The array dimensions are returned as a Perl array reference and the datatype as a C object. my $arr1 = Array_Type->new( "Int_Type", [3,2] ); my ( $dims, $ndims, $type ) = $arr1->array_info(); print "Num of dims = $ndims and type = $type\n"; print "Dims = [ " . join( ", ", @$dims ) . " ]\n"; =item * C Returns the data value stored at the given location. my $arr1 = Array_Type->new( "Int_Type", [3,2] ); my $val = $arr1->get(1,0); =item * C Sets the data value stored at the given location to the specified value. It returns this value. my $arr1 = Array_Type->new( "Int_Type", [3,2] ); $arr1->set(1,0,-43); Note that there is no check made that the datatype of the new value matches that of the array. You will find out about this when you try to convert the array to S-Lang. Probably with a rather unfriendly message. =item * C Since the accessor functions described above are very limited, the C can be used to get an array reference to the actual data. This can then be used as you would any other Perl array reference. If you change the array size then you are going to make the object unhappy, so take care. my $arr1 = Array_Type->new( "Int_Type", [3,2] ); my $dref = $arr1->toPerl(); $$dref[1][0] = -4; =back Further methods could be added to improve the array access - such as the C S-Lang function and means of accessing array slices - but this is unlikely to happen since PDL has a I richer set of operations (although they only work for numeric types), or you can do the array processing in S-Lang. =head1 SEE ALSO L, L, L, L