We don't bite newbies here... much PerlMonks

### subroutine help

by perlguru22 (Acolyte)
 on Nov 29, 2012 at 06:26 UTC Need Help??
perlguru22 has asked for the wisdom of the Perl Monks concerning the following question:

I am having some problems passing my array to my subroutine.
```
print " enter a formula.\n";
chomp(\$form=<>);

@array = \$form =~ /[A-Z][a-z]?|\d+/ig

\$weight = getweight(@array);

sub getweight
{
my @weight =@_;
}

So if the User enters like C12H22O11 when I print at array it shows C 12 H 22 O 11 but when I print or return @weight it goes back to C12H222O11 its no longer seperated do I need to do the reg expression inside the routine too? Thanks I hope someone can show me the right way

Replies are listed 'Best First'.
Re: subroutine help
by NetWallah (Canon) on Nov 29, 2012 at 06:53 UTC

Your problems have to do with converting between the returned array, and the scalar \$weight you assign it to.

Please search this site for responses to Your and your collegues VERY SIMILAR questions over the last few days, and avoid duplicate questions.

"By three methods we may learn wisdom: First, by reflection, which is noblest; Second, by imitation, which is easiest; and third by experience, which is the bitterest."           -Confucius

One wonders if the whole college is signed up to PerlMonks :-)
A Monk aims to give answers to those who have none, and to learn from those who know more.
Sorry I will make sure to look next time
Re: subroutine help
by CountZero (Bishop) on Nov 29, 2012 at 07:16 UTC
When you print an array, like print @array; then all elements of the array are printed next to each other without any separation.

On the other hand if you print the array like print "@array"; then the array is first interpolated, i.e. the elements are joined by whatever is in \$" (usually a space).

CountZero

A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

My blog: Imperial Deltronics
without any separation
Well, the members of the list are separated by \$, which is empty by default.
لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ
Re: subroutine help
by frozenwithjoy (Priest) on Nov 29, 2012 at 07:05 UTC

• Your code doesn't compile, because there is a semi-colon missing at the end of @array = \$form =~ /[A-Z][a-z]?|\d+/ig
• You assign the result of getweight(), which is in list context, to a scalar. As a result, \$weight just equals 6 (the number of elements in the array). If I were you, I'd skip the subroutine and array and make weight a hash directly from the get-go:
```#!/usr/bin/env perl
use strict;
use warnings;
use feature 'say';
use Data::Printer;

print "Enter a chemical formula: ";
chomp( my \$form = <> );

my %weight = \$form =~ /[A-Z][a-z]?|\d+/ig;
p %weight;

__END__

Enter a chemical formula: C12H22O11
{
C   12,
H   22,
O   11
}
Sorry I think my kindle modified it and removed the ;
Oh, those pesky kindles! (I wonder whatever happened to the 'dog ate my semi-colon' excuse?)
Re: subroutine help
by space_monk (Chaplain) on Nov 29, 2012 at 07:37 UTC
I'm sure it's been done before in the other answers, and maybe even more efficiently, but this is what I think you're trying to get to:
```#!/bin/perl
#if your program doesn't begin with this, you deserve to fail...
use strict;
# Data::Dumper not used in example,
# but you always need it when you expand the module :-)
use Data::Dumper;

# you can either hardcode all atomic weights in ...
# ..or read them from a data file/ database
my %weights = (
'C' => 12.0,
'O' => 16.0,
'Co' => 58.933195,
'H' => 1.008
);

my @test = ( 'C12H22O11' );

for my \$form (@test) {
my @array = \$form =~ /[A-Z][a-z]*\d*/ig;

my \$weight = getweight(@array);
print "Weight of @array : \$weight\n";
}

sub getweight {
my @atoms =@_;
my \$weight = 0.0;

for my \$element (@atoms) {
if (\$element =~ /([A-Z][a-z]*)(\d*)/) {
my \$eweight = \$weights{\$1};
my \$mult = \$2 || 1;
\$weight += (\$eweight*\$mult);
print STDERR "Adding \$1 (\$eweight)* \$mult\n";
}
}
print STDERR "Molecule: @atoms\n";

return \$weight;
}

Feel free to modify for your purposes and correct naming,read atomic weights from a file etc.

Output from above program:

```Adding C (12)* 12
Molecule: C12 H22 O11
Weight of C12 H22 O11 : 342.176

Updated to reflect the fact that some transuranic elements have 3 letter identifiers, not just two. :-)

Also updated to reflect suggestions by Kenosis and others below....

Whilst adequate for the indicated input, the method of parsing and atomic weight calculation is a little lightweight and basic, and doesn't handle more complex molecules, so consider using Chemistry::Mol or a similar package if you want to get round the flaws that have been indicated below....

A Monk aims to give answers to those who have none, and to learn from those who know more.

A few evil examples:

• Ca(OH)2
• CH3CH2CH2CH2CH2CH2CH3
• CH3[CH2]5CH3
• CH3[CH2]nCH3 - not really calculable, but should be parsable
perl -E'sub Monkey::do{say\$_,for@_,do{(\$monkey=[caller(0)]->[3])=~s{::}{ }and\$monkey}}"Monkey say"->Monkey::do'
Yes, alright my solution is lightweight, only getting the second of your examples right, lets use Chemistry::Mol to get it right properly :-)
A Monk aims to give answers to those who have none, and to learn from those who know more.

Consider the following minor modifications:

```'Co' => 58.933195

my @array = \$form =~ /[A-Z][a-z]*\d*/g;

if (\$element =~ /([A-Z][a-z]*)(\d*)/) {

my \$mult = \$2 || 1;
\$weight += (\$eweight*\$mult);
print STDERR "Adding \$1 (\$eweight)* \$mult\n";

Run on CoCO2:

```Adding Co (58.933195)* 1
Molecule: Co C O2
Weight of Co C O2 : 102.933195
Don't forget your units: g/mol! :P
Its 30 years since I did Chemistry 'A' Level....be grateful I remembered this much. :-)
A Monk aims to give answers to those who have none, and to learn from those who know more.
Thank you so much this actually explains a lot I was heading the wrong way thanks =)
Re: subroutine help
by Anonymous Monk on Nov 30, 2012 at 20:28 UTC
The arguments to a subroutine are ... a list. If you "pass an array," Perl might flatten that array into a list of individual arguments. What you really want to do is to pass one argument to the subroutine, consisting of a reference to that array; an "arrayref." A reference (to anything...) is always "one thing." Be explicit about how you say this sort of thing to Perl, because Perl does try to "do what I mean (DWIM)" and is sometimes wrong. Also be sure to say use strict; use warnings; at the top of your module so that problems which might otherwise "helpfully" be ignored will be told to you.

Create A New User
Node Status?
node history
Node Type: perlquestion [id://1006165]
Approved by mbethke
Front-paged by 2teez
help
Chatterbox?
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others exploiting the Monastery: (3)
As of 2018-05-20 11:03 GMT
Sections?
Information?
Find Nodes?
Leftovers?
Voting Booth?
World peace can best be achieved by:

Results (150 votes). Check out past polls.

Notices?