http://www.perlmonks.org?node_id=1025208

tfredett has asked for the wisdom of the Perl Monks concerning the following question:

Hello, I know in some languages, such as java it is possible to create a unique variable type. What I mean by that, is you can create a variable type with a name of your choice, and it can contain different data types already defined. An example: you could create a variable type named foo, and in it, have it place two other variable types, a Boolean variable, and an int. Then anytime from then on in your code, if you create a variable of type foo, it will have in it a Boolean and a int.

The reason I am looking for this, is I am working on solving the queens problem, and my current algorithm, requires I create a two-dimensional array of the above described variable type foo. The Boolean to represent if a queen is present in a square, and the int to represent if a square is threatened by a queen. I however after looking through both perldocs and doing several different Google searches, have come up with no results that help resolve my predicament. I was hoping someone would be able to point me in the correct direction if this is indeed possible, or to be informed that this is not possible, so I may begin to contemplate an alternative.

I also ask for no help in solving the queens problem specifically, I am viewing this as a intellectual challenge, and receiving help in solving the actual problem I feel would defeat the purpose. Instead, the place where I have hit the roadblock, in this case, the creation of a new variable type. Any help is appreciated.

Replies are listed 'Best First'.
Re: Creating a unique variable type (an object)
by Anonymous Monk on Mar 25, 2013 at 01:07 UTC

      An object is certainly the answer to your question about how to create your own variable type, however you could also meet your needs by using the existing types if your program is a one off that only you will edit/maintain/read and you'd like to get to a solution prior to digging into perltoot for the first time (although you really should read this).

      You could use an arrayref for your variable and push the Boolean value onto the first spot in the array, then push the int onto the second. You then access these later knowing that the Boolean is always first and the int second. Incidentally this could be a model for your future object.

      my $foo = [ ]; push @{$foo}, 1; push @{$foo}, 23; my $boolean = ${$foo}[0]; my $int = ${$foo}[1];

        I could do the idea of having the boolean in one element and having the int in the following. Frankly though, I find it easier to manage and think about if I could have them both the boolean and the int in the same element. Reasoning for this, is I will have to have the system calculate different locations in a later portion of the code, and to just find a single element via very simple arithmetic is trivial, compared to doing a similar calculation, but then having to worry considering the next element, which will in essence throw off my calculation.

        What I mean by this is I have several subroutines that currently depend on a simple calculation, in this case, lets take an example of placing a queen on D6, well with my two-dimensional array, I will completely block out the vertical and the horizontal columns, but what about the diagonals? well by doing a simple addition or subtraction of 1 to the factor being used in this case (in this case its actually two factors, the D which is equal to 4, and another that 6) With this, I could do a simple subtraction on one and addition on the other or two additions or two subtractions until I run out of possible squares. In our example, to resolve the upper left portion of the diagonal, we would do a double subtraction of 1 until one of the two arrays runs out of elements, and so on. This is why I want to have both of these values in the same element, since if they happen to be in the following element, I can no longer use the method described above to calculate all the squares being threatened by that queen that has been placed, in this case at D6, since as I go in one direction, I may be accessing either the boolean or the int, and I can't be sure without modifying the math used. It is just easier in this case to have them both in the same element if possible. The only other solution which could be used, is to create two two-dimensional arrays, and while that would work, I feel it would be a waste of resources that could be easily avoided.

        A rough diagram of the board setup with the Q being the queen and the x's being the squares under attack, has been created below.

        1 2 3 4 5 6 7 8 A | | |x| | |x| | | B | | | |x| |x| |x| C | | | | |x|x|x| | D |x|x|x|x|x|Q|x|x| E | | | | |x|x|x| | F | | | |x| |x| |x| G | | |x| | |x| | | H | |x| | | |x| | |
      Thank you for that, the name definitely helped, I guess I just had a brain fart if you will, but my issue is now resolved. Thank you again.
Re: Creating a unique variable type
by Laurent_R (Canon) on Mar 25, 2013 at 20:07 UTC
    You could probably use an array of arrays (AoA), an array of hashes (AoH), an hash or arrays (HoA) or a hash of hashes (HoH). Your could even use 2 arrays, or 2 hashes, or an array and a hash, but that is sort of less interesting. You could even use a simple array (or a simple hash) and concatenate the Boolean and the int into it, but that starts to be quite ugly.
      I was under the impression you couldn't place an array inside another array with Perl, am I perhaps incorrect in this assumption? I only say this, because I actually considered doing that using that in a similar sense, but after some research, found that Perl doesn't really like doing that.

        Technically, you are correct. An array value can only hold a scalar variable.

        However, there are a couple of ways you can work around that. The most common is references. Instead of storing an array inside of another array, you store array references (which are scalar values) in your array.

        The easiest way would be an array of array references:

        my @array = ( "a", "b", "c" ); my $aref = [ "x", "y", "z" ]; push @array, $aref; # Current value for @array: #@array = ( # "a", # "b", # "c", # [ # "x", # "y", # "z" # ], # )

        A brief example of using an array of array references:

        #!/usr/bin/env perl use strict; use warnings; my @array; while (<DATA>) { chomp; my ($bool,$int) = split ","; # Take each ($bool,$int) tuple, and store it in an array reference # (By placing it in []) that we add to our array push @array, [ $bool, $int ]; } # Cycle through our array of array references and print them foreach my $entry (@array) { print "Bool: " . $entry->[0] . "\n"; # The -> dereferences our ar +rayref print "Int: " . $entry->[1] . "\n\n"; # The -> dereferences our ar +rayref } __DATA__ 0,14 0,7 1,13 0,3 1,9 0,2 1,1

        Giving us the following output:

        perl-fu@nexus:~/foo$ ./a_of_a.pl Bool: 0 Int: 14 Bool: 0 Int: 7 Bool: 1 Int: 13 Bool: 0 Int: 3 Bool: 1 Int: 9 Bool: 0 Int: 2 Bool: 1 Int: 1

        For more information on nested data structures using references, you should check out perlreftut and perlref.

        Christopher Cashell

        Your are right, you can't really place an array inside another array with Perl, but you can put in an array a reference toward another array. And you can do it in a way that you don't have to be explicit about the existence of a reference, it really looks like a two-dimensional array.

        Suppose you want to store in an array the full name and the abbreviated name of each of the months. Your could do this in various syntactic ways as follows:

        $month[0][0] = "January"; $month[0][1] = "Jan"; $month[1] = ["February", "Feb"]; push @month, ["March", "Mar"]; push @{$month[3]}, "April"; push @{$month[3]}, "Apr";
        If you dump the content of @month under the debugger, you get the following structure:
        0 ARRAY(0x20381390) 0 'January' 1 'Jan' 1 ARRAY(0x20381528) 0 'February' 1 'Feb' 2 ARRAY(0x203815d0) 0 'March' 1 'Mar' 3 ARRAY(0x204424d0) 0 'April' 1 'Apr'

        The dump shows that the @month array contains 4 references to arrays (the 'ARRAY(0x20381390)' part), and that each of these arrays contain two elements, the month full and short names. And you can just print one element as follows:

        print $month[3][1]; # prints "Apr"

        So you could almost use all this without even knowing about references. Except that if you don't understand the underlying references, you will not be able to understand what's happening when something goes wrong (say, when the compiler complains about some illegal construct or, worse, when your program is not doing what you want because your data is not quite what you think).

        In brief, you can in principle construct an entire program with AoA (or AoH, HoA or HAH) without ever using a syntax that explicitly shows that the top-level array contains in fact references to other (anonymous) arrays. But you still need to know and understand this reference shebang if you are going to use it for anything else than an extremely simple program.

        > I was under the impression you couldn't place an array inside another array with Perl, am I perhaps incorrect in this assumption?

        Of course you can, the irritation stems from the fact that Perl has two representations of complex variables, the standard "list" form with sigil and the "scalar" reference form.

        You need to use the reference form, because elements need to be scalars.

        Cheers Rolf

        ( addicted to the Perl Programming Language)

Re: Creating a unique variable type
by sundialsvc4 (Abbot) on Mar 25, 2013 at 12:57 UTC

    Perl is not a “strongly typed” language.   You cannot now say my $foo : integer;

      Perl is not a “strongly typed” language.

      True for some definitions of “strongly typed“ (see Strong_typing), but in this context I think it’s clearer if we describe Perl as dynamically typed, in contrast to languages like C which are statically typed. With static typing, a variable’s type is known at compile time; with dynamic typing, a (scalar) variable’s data type is determined at runtime, and may change as the script executes — $foo may hold a string, then an integer, then a reference, ...

      You cannot now say my $foo : integer;

      Well, not in core Perl, no. But if you have objects, you’ll probably be using an object system (and if not, please see the picture on Your Mother’s home page!), and in an object system like Moose you can certainly specify that $foo is an Int. Here is one way to approach the OP’s task using Moose:

      But this is still dynamic typing: if a type mismatch occurs, it is a runtime error which results.

      Hope that helps,

      Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

        There is, indeed, not a single phrase in programming vernacular that has but one unambiguous meaning ... except maybe “crash.”

      > You cannot now say my $foo : integer;

      O RLY?

      see attributes for enabling

      my $foo :integer;

      and the parser also allows "typing"

      DB<105> {package Int} DB<106> my Int $x => undef

      ... though w/o much benefit.

      Cheers Rolf

      ( addicted to the Perl Programming Language)

      I understand that, I was primarily using the int and boolean as an example, I understand they both would be considered scalars until I assign them some value. The main point I was trying to get across was how I could get both those data types into one unit, in this case an object. The usefulness of this, for my particular case, is quite useful for the application I have desired for it. It should make the data easier to manage.