Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW
 
PerlMonks  

subroutine help

by perlguru22 (Acolyte)
on Nov 29, 2012 at 06:26 UTC ( [id://1006165]=perlquestion: print w/replies, xml ) 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 code does not compile, and your problem description is wrong.

    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

    A few things to help you out:

    • 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 Adding H (1.008)* 22 Adding O (16)* 11 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 Adding C (12)* 1 Adding O (16)* 2 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.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1006165]
Approved by mbethke
Front-paged by 2teez
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others having an uproarious good time at the Monastery: (4)
As of 2024-03-19 02:07 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found