XML::LibXML - detect if two Elements are the same? - perl

I'm working with XML::LibXML in Perl.
Say I have two $element references gotten by different (opaque) XPath queries.
(How) can I determine, if the two $element (Node) refs are the same element in the document tree?
Comparing $el1 == $el2doesn't always work as far as I could tell.

It's in the XML::LibXML::Node documentation:
isSameNode
$bool = $node->isSameNode( $other_node );
returns TRUE (1) if the given nodes refer to the same node structure,
otherwise FALSE (0) is returned.

Related

Two-sided references in a list

I'm just starting to learn Perl and I have to do an exercise containing references.
I have to create a program, that constructs a list with two sided references, that are are received as command line arguments. At the beginning of the program, there is only one element in the list - 0. To go through the list, reference is being used, that references to the only element of the list at the moment - 0. The arguments of the command line are being read one by one and added right behind the element, that is being referenced to. When one argument is added, the reference slides one element to the right(it references to the newly added element). There are also two special elements - + and -. + allows the reference to move one element to the right, and - one element to the left. Also, it is important that the reference would not go beyond the list limit.
The output is all the arguments in the correct order of the list.
Additional requirements are that the list must be created by using hashes, that contain links to neighbouring elements. Also, I cannot use arrays to store the whole list.
There are a few examples to make it easier to grasp the concept, this is the most useful one:
./3.pl A D - B C + E
0 A B C D E
All I've got now is just the start of the program, it is nowhere near done and doesn't compile, but I can't figure out where to go from there. I've tried looking for some information about two-sided references(I'm not sure if I'm translating it correctly), but I can't seem to find anything. Any information about two-sided references or any tips how to start writing this program properly would be very appreciated.
My code:
#!/usr/bin/perl
use strict;
use warnings;
my $A= {
value=>'0',
prev=>'undef',
next=>'$B'
};
my $B= {
value=>'0',
prev=>'$A',
next=>'$C'
};
my $C= {
value=>'0',
prev=>'$B',
next=>'undef'
};
for my $smbl(0..#$ARGV) {
$A-> {value} = $ARGV[$smbl];
$Α-> {next} = $ARGV[$smbl+1];
}
First of all, what you are building is called a doubly linked list.
Let me tell you the biggest trick for working with linked lists: Create a dummy "head" node and a dummy "tail" node. You won't print their values, but having them will greatly reduce the number of special cases in your code, making it so much simpler!
At the core, you will have three "pointers" (references).
$head points to the first node of the list.
$tail points to the last node of the list.
$cursor initially points to the node in $tail. New nodes will be inserted before this node.
When processing +, two different situations you need to handle:
$cursor == $tail: Error! Cursor moved beyond end of list.
$cursor != $tail: Point $cursor to the node following the one it references.
When processing -, there are two different situations you need to handle:
$cursor->{prev} == $head: Error! Cursor moved beyond start of list.
$cursor->{prev} != $head: Point $cursor to the node preceding the one it references.
When processing inserting nodes, no checks need to be performed because of the dummy nodes!

How can I get unique members per category using perl?

I used a hash to store members id. I avoid the duplicity using the following validation:
if($cases{$type} != $member_id) {
$cases{$type} = $member_id;
}
After that, I want to get the total of members by each $type. How can I do that?
I tried the following but do not work:
scalar(values $cases{$type});
Thanks a lot.
Your current approach doesn't actually count how many members are in a given type; it just keeps track of what the last member added is.
To keep a count, consider using a hash-of-a-hash; use this for each $member_id you're adding:
$cases{$type}{$member_id} = 1;
The value for the hash assignment doesn't matter; it's just a way to use hash keys to maintain uniqueness.
Then, to get the number of members for a given type, use
scalar keys %{$cases{$type}}
You could use $member_id as the key and use grep to find the count.
Store the $type like $case{$member_id} = $type
Use the following to print the count.
print scalar grep $case{$_} == $type, keys %case;

Using hash as a reference is deprecated

I searched SO before asking this question, I am completely new to this and have no idea how to handle these errors. By this I mean Perl language.
When I put this
%name->{#id[$#id]} = $temp;
I get the error Using a hash as a reference is deprecated
I tried
$name{#id[$#id]} = $temp
but couldn't get any results back.
Any suggestions?
The correct way to access an element of hash %name is $name{'key'}. The syntax %name->{'key'} was valid in Perl v5.6 but has since been deprecated.
Similarly, to access the last element of array #id you should write $id[$#id] or, more simply, $id[-1].
Your second variation should work fine, and your inability to retrieve the value has an unrelated reason.
Write
$name{$id[-1]} = 'test';
and
print $name{$id[-1]};
will display test correctly
%name->{...}
has always been buggy. It doesn't do what it should do. As such, it now warns when you try to use it. The proper way to index a hash is
$name{...}
as you already believe.
Now, you say
$name{#id[$#id]}
doesn't work, but if so, it's because of an error somewhere else in the code. That code most definitely works
>perl -wE"#id = qw( a b c ); %name = ( a=>3, b=>4, c=>5 ); say $name{#id[$#id]};"
Scalar value #id[$#id] better written as $id[$#id] at -e line 1.
5
As the warning says, though, the proper way to index an array isn't
#id[...]
It's actually
$id[...]
Finally, the easiest way to get the last element of an array is to use index -1. The means your code should be
$name{ $id[-1] }
The popular answer is to just not dereference, but that's not correct. In other words %$hash_ref->{$key} and %$hash_ref{$key} are not interchangeable. The former is required to access a hash reference nested as an element in another hash reference.
For many moons it has been common place to nest hash references. In fact there are several modules that parse data and store it in this kind of data structure. Instantly depreciating the behavior without module updates was not a good thing. At times my data is trapped in a nested hash and the only way to get it is to do something like.
$new_hash_ref = $target_hash_ref->{$key1}
$new_hash_ref2 = $target_hash_ref->{$key2}
$new_hash_ref3 = $target_hash_ref->{$key3}
because I can't
foreach my $i(keys(%$target_hash_ref)) {
foreach(%$target_hash_ref->{$i} {
#do stuff with $_
}
}
anymore.
Yes the above is a little strange, but creating new variables just to avoid accessing a data structure in a certain way is worse. Am I missing something?
If you want one item from an array or hash use $. For a list of items use # and % respectively. Your use of # as a reference returned a list instead of an item which perl may have interpreted as a hash.
This code demonstrates your reference of a hash of arrays.
#!/usr/bin perl -w
my %these = ( 'first'=>101,
'second'=>102,
);
my #those = qw( first second );
print $these{$those[$#those]};
prints '102'

Meaning of NEXT in Linked List creation in perl

So I am trying to learn Linked Lists using Perl. I am reading Mastering Algorithms with Perl by Jon Orwant. In the book he explains how to create a linked list.
I understand most of it, but I just simply fail to understand the command/index/key NEXT in the second last line of the code snippet.
$list=undef;
$tail=\$list;
foreach (1..5){
my $node = [undef, $_ * $_];
$$tail = $node;
$tail = \${$node->[NEXT]}; # The NEXT on this line?
}
What is he trying to do there?
Is $node a scalar, which stores the address of the unnamed array? Also even if we are dereferencing $node, should we not refer to the individual elements by an index number, such as (0,1). If we do use NEXT as a key, is $node a reference to a hash?
I am very confused.
Something in plain English will be highly appreciated.
NEXT is a constant, declared earlier in the script. It contains an integer value representing the index of the current node's member element that refers to the next node.
Under this scheme, each node is a small anonymous array. One element of this anonymous array contains the payload, and the other contains a reference pointing to the next node.
If you look at some of the earlier examples in that chapter you will see the following declarations:
use constant NEXT => 0;
use constant VAL => 1;
So $node->[NEXT] is synonymous to $node->[0], which contains a reference to the next node in the linked list chain, while $node->[VAL] is synonymous with $node->[1]; the value (or payload) stored in the current node.
I'll comment on the code snippet you provided:
foreach (1..5){
my $node = [undef, $_ * $_]; # Create a new node as an anon array.
# Set the previous node's "next node reference" to point to this new node.
$$tail = $node;
# Remember a reference to the new node's "next node reference" element.
# So that it can be updated when another new element is added on next iteraton.
$tail = \${$node->[NEXT]}; # The NEXT on this line?
}
Excellent book, by the way. I've got several algorithms books, and that one continues to be among my favorites after all these years.
Update: I do agree that the book isn't a model of current idiomatic Perl, or current "best practices" Perl, but do feel it is a nice resource for gaining an understanding of the application of classic algorithms with Perl. I still refer back to it from time to time.
NEXT is a constant, declared on an earlier page, that contains a number. Its being used instead of just the regular number to access the array ref $node so the reader knows that slot is where the next element in the linked list is stored.
It's a technique to use array references to store things other than lists. The technique was intended to save memory and CPU time compared to using a hash reference. In reality it doesn't make much performance difference and its awkward to work with. The book is quite a bit out of date in its ideas about how to write Perl code. Use a hash reference instead.
my $list;
my $tail = \$list;
foreach my $num (1..5) {
my $node = { data => $num };
$$tail = $node;
$tail = \$node->{next};
}

Why do all of these methods of accessing an array work?

It seems to me that some of these should fail, but they all output what they are supposed to:
$, = "\n";
%test = (
"one" => ["one_0", "one_1"],
"two" => ["two_0", "two_1"]
);
print #{$test{"one"}}[0],
#{$test{"one"}}->[0],
$test{"two"}->[0],
$test{"one"}[1];
Why is this?
Your first example is different than the others. It is an array slice -- but in disguise since you are only asking for one item.
#{$test{"one"}}[0];
#{$test{"one"}}[1, 0]; # Another slice example.
Your other examples are alternative ways of de-referencing items within a multi-level data structure. However, the use of an array for de-referencing is deprecated (see perldiag).
#{$test{"one"}}->[0]; # Deprecated. Turn on 'use warnings'.
$test{"two"}->[0]; # Valid.
$test{"one"}[1]; # Valid but easier to type.
Regarding the last example, two subscripts sitting next to each other have an implied -> between them. See, for example, the discussion of the Arrow Rule in perlreftut.