Re: one line to check for common list values
by haukex (Archbishop) on Feb 25, 2017 at 19:24 UTC
|
Welcome to Perl and the Monastery, dollar_sign! Just a small tip on posting: Thank you for using <code> tags for the code, but please don't add line numbers.
Perl has excellent documentation, see perldoc.perl.org, and the command line tool perldoc. For example, typing perldoc -q intersection at the command line brings up this FAQ: How do I compute the difference of two arrays? How do I compute the intersection of two arrays?
One of the pieces of Perl philosophy is TIMTOWTDI, There Is More Than One Way To Do It, so if you want to use a regex to find matches, that's ok, but see the performance comparison below. Note that if your input wasn't limited to integers, your regex has one potential problem, see what happens when you set my @input = (1.23) and my @valuesToMatch = (23). I wrote about the generation of regexes here (still a draft at the moment), and I showed what I would consider a "safer" version of your code below.
I would also like to know if using regexps for this is less or more efficient (performance-wise) than just using a for loop.
The go-to module for this is Benchmark. Here is a comparison of the performance of using regular expressions, hashes, and grep. Note how the hash approach clearly wins!
Would it be possible to reduce the amount of code necessary to find the common values between the two arrays to only one line of Perl?
Sure, it's possible to jam it all into one line, the AM showed one way to do it for the "regex" approach (Update: tybalt89 showed a few more, but benchmark them and you'll see all three of them are less efficient than all of the above). It's also possible in the above "grep" solution using two nested greps, but for the "hash" solution, you'll need multiple statements, even if you jam them all into one line :-)
I had no idea where to look for up to date Perl-specific exercises
One might be HackerRank, or perhaps Rosetta Code.
| [reply] [d/l] [select] |
|
|
HackerRank is fun, but many of the problems are rigged to favor C solutions. Also check out Project Euler (projecteuler.net).
| [reply] |
Re: one line to check for common list values
by 1nickt (Canon) on Feb 25, 2017 at 21:21 UTC
|
Hi dollar_sign, and welcome to the Monastery and to Perl, the One True Religion.
Some of the brethren have shown you some valuable tips regarding your posted code, especially regarding regexps and hashes. I offer a slightly different answer.
write a program that returns a list that contains only the elements that are common between the lists (without duplicates). Make sure your program works on two lists of different sizes.
perl -Mstrict -MArray::Utils=:all -WE 'my @x=(1,1,2,3,4,3); my @y=(0,3
+,4,5,6,3,7,8); say for unique intersect @x, @y'
Returns:
3
4
I understand that you are teaching yourself to program by solving simple problems, and the use of intersect() and unique() from a library (Array::Utils) might seems like it won't teach you anything about list reduction. But I submit that a key part of programming is knowing how to find and use libraries for common tasks -- precisely so that you can move on to learning how to solve more complex problems or accomplish more complex tasks.
One of the greatest benefits of using Perl is the CPAN, where you can find tools from bone saws to X-ray-guided arthroscopic laser scalpels. Learning how to use those tools to perform an operation is also learning to program.
Hope this helps!
The way forward always starts with a minimal test.
| [reply] [d/l] [select] |
Re: one line to check for common list values
by tybalt89 (Monsignor) on Feb 25, 2017 at 19:46 UTC
|
#!/usr/bin/perl
use strict;
use warnings;
# This script should check what elements are common between two lists
# Lists of random numbers
my @input = map { int rand $_ } 1..100;
my @valuesToMatch = map { int rand $_ } 1..100;
print "-----list 1 is:-----\n@input\n-----and list 2 is:-----\n@values
+ToMatch\n";
# Match the common values (three ways)
my @result = "@input\n@valuesToMatch" =~ /\b(\d+)\b(?=.*\n.*\b\1\b)/g;
print "---------------The matching numbers are:---------------\n@resul
+t\n" ;
@result = grep +{map { $_, 1 } @valuesToMatch}->{$_}, @input;
print "---------------The matching numbers are:---------------\n@resul
+t\n" ;
@result = grep "@valuesToMatch" =~ /\b$_\b/, @input;
print "---------------The matching numbers are:---------------\n@resul
+t\n" ;
| [reply] [d/l] |
Re: Re: one line to check for common list values
by dollar_sign (Novice) on Feb 25, 2017 at 21:10 UTC
|
First of all, thank you all very much for your replies and for the warm welcome, I have a feeling that I will be returning here a lot in the foreseeable future :).
Your explanations are a huge help, I have been reading the perldoc a lot the past couple of days, but seeing code in a context I have been working in just clears up so much more for me! I will study all of your answers thoroughly until I understand every last bit of code in them!
I'm sorry about the line numbering, I am using a version of VIM that has no clipboard support so I have to copy my whole command line screen, including the line numbers (still need to look for some plugin/other version of VIM, in the meantime I guess I'll just open my code in another editor to paste it here :).
| [reply] |
|
|
:set nu
:set nonu
The way forward always starts with a minimal test.
| [reply] [d/l] [select] |
Re: one line to check for common list values
by Anonymous Monk on Feb 25, 2017 at 18:12 UTC
|
There's a trick for jamming an expression into a string or regex:
my @result = "@input" =~ /@{[join "|", map "\\b$_\\b", @valuesToMatch]}/g;
But you're probably better off using a hash for this. It'll be much faster if the arrays are large.
my %match;
$match{$_} = 1 foreach @valuesToMatch;
my @result = grep $match{$_}, @input;
| [reply] [d/l] [select] |
Re: one line to check for common list values
by Lotus1 (Vicar) on Feb 27, 2017 at 00:23 UTC
|
I would like to point out that the numbers aren't as random as they could be. int(rand(1)) will always produce 0 as the output. int(rand(2)) will produce 0 or 1, int(rand(3)) will produce 0, 1 or 2 and so on. If you would like to produce multiple random numbers that are between 1 0 and 99 then try this.
use strict;
use warnings;
my @a;
foreach my $upper (1..10) {
@a = map { int rand $upper } 1..10;
print "@a\n";
}
print '*'x79, "\n";
foreach my $upper (1..10) {
@a = map { int rand 100 } 1..10;
printf "%2s ", $_ foreach @a;
print "\n";
}
__END__
0 0 0 0 0 0 0 0 0 0
0 1 1 0 0 1 1 1 1 1
2 0 0 2 1 1 1 0 2 1
1 0 0 0 3 0 1 0 0 2
2 2 1 1 4 1 3 4 2 0
4 3 3 2 0 4 5 1 2 3
0 0 5 5 2 6 5 1 1 6
1 4 7 5 2 0 2 3 6 7
7 4 0 4 4 8 2 2 1 1
8 9 2 9 7 6 0 7 1 4
**********************************************************************
+*********
15 38 19 5 43 30 71 4 55 44
12 62 64 68 38 3 76 68 61 41
11 60 88 46 80 89 20 45 78 99
91 57 58 16 93 75 69 98 90 81
87 61 42 72 47 16 19 19 97 16
51 99 89 12 86 53 58 71 52 99
31 1 36 27 85 56 90 88 69 92
82 6 86 59 88 66 88 50 42 58
23 54 96 52 61 39 82 65 62 6
7 92 77 22 78 91 6 63 67 22
| [reply] [d/l] [select] |
|
|
Lotus1 Thank you for pointing that out!
1nickt Thank you for your advice, I will surely be taking advantage of CPAN when I start doing bigger projects. At the moment my priority is with leaning all the builtins because in addition to using Perl for building applications, I want to be able to write portable scripts. (I might want to do some sysadmin work in the future and I was told that portability is key in that line of work).
| [reply] |
Re: one line to check for common list values
by Cow1337killr (Monk) on Mar 04, 2017 at 15:18 UTC
|
I've been practicing programming in Perl today, using exercises from practicepython.org...
LOL Are you a masochist?
Go to the local library and get a copy of https://www.amazon.com/Learning-Perl-Randal-L-Schwartz/dp/1449303587, or an older edition of the Learning Perl book. You can read it cover to cover, or try to use it as a reference book. When you are done with that, you can try Intermediate Perl: Beyond The Basics of Learning Perl 2nd Edition or https://www.amazon.com/Beginning-Perl-Curtis-Poe/dp/1118013840
Or, one can learn online. For example, this popped up when I Googled "perl arrays": https://perlmaven.com/perl-arrays.
Try things out. If they don't work, start a thread at PerlMonks. Weekends tend to be slow. During the week, there are more people here. There is so much experience in this community that it is unbelieveable.
You can try this area of PerlMonks: Tutorials.
Then, there is the Perl documentation. The easiest way to get to it is on the Internet. For example, http://perldoc.perl.org/perlintro.html#Perl-variable-types. However, these tend to be rather dense, rather than pedagogical.
| [reply] |
|
|
Cow1337killr hahaha well I guess I had that question coming ;) I actually ordered a copy of Learning Perl (7th edition) from a a web store just 4 hours ago! (Don't tell anyone but I got excited and found a pdf of it on the internet, so I could start reading while waiting for the physical book)
| [reply] |