perl -e '%h=(a=>1,b=>2); for (keys %h) { s/a/b/ }; print keys %h'

Discussion in 'Perl Misc' started by A. Farber, Jun 11, 2004.

  1. A. Farber

    A. Farber Guest

    Hi,

    I have a hash where keys and values are file paths.
    I'm going to use that hash to generate a GNU Makefile.
    For some parts of the file paths I have shortcuts
    (like $(TOPDIR)) which I'd like to substitute into the
    paths. I.e. I'd like to go through the hash keys and
    perform a substitution on them, like in this test case:

    perl -e '%h=(a=>1,b=>2); for (keys %h) { s/a/b/ }; print keys %h'
    ab

    The code above doesn't work as expected - I'd need it to
    print "bb" and not "ab". Looks like the hash keys aren't
    the "lvalue" described in the "perldoc perlsyn":

    If any element of LIST is an lvalue, you can modify it by
    modifying VAR inside the loop. Conversely, if any element
    of LIST is NOT an lvalue, any attempt to modify that ele-
    ment will fail. In other words, the "foreach" loop index
    variable is an implicit alias for each item in the list
    that you're looping over.

    What could I do to solve this problem, please?

    I'd prefer a solution with for() or while() because
    I need to perform few more operations on the hash keys.

    Regards
    Alex
    A. Farber, Jun 11, 2004
    #1
    1. Advertising

  2. A. Farber

    Ben Morrow Guest

    Quoth (A. Farber):
    >
    > I have a hash where keys and values are file paths.
    > I'm going to use that hash to generate a GNU Makefile.
    > For some parts of the file paths I have shortcuts
    > (like $(TOPDIR)) which I'd like to substitute into the
    > paths. I.e. I'd like to go through the hash keys and
    > perform a substitution on them, like in this test case:
    >
    > perl -e '%h=(a=>1,b=>2); for (keys %h) { s/a/b/ }; print keys %h'
    > ab
    >
    > The code above doesn't work as expected - I'd need it to
    > print "bb" and not "ab".
    >
    > What could I do to solve this problem, please?


    You could try (untested)

    my %h = ( a => 1, b => 2 );
    %h = map {
    (my $k = $_) =~ s/a/b/;
    ( $k => $h{$_} );
    } keys %h;

    > I'd prefer a solution with for() or while() because
    > I need to perform few more operations on the hash keys.


    You can do what you like to the keys inside the map loop as well.

    Ben

    --
    All persons, living or dead, are entirely coincidental.
    Kurt Vonnegut
    Ben Morrow, Jun 11, 2004
    #2
    1. Advertising

  3. A. Farber

    Anno Siegel Guest

    A. Farber <> wrote in comp.lang.perl.misc:
    > Hi,
    >
    > I have a hash where keys and values are file paths.
    > I'm going to use that hash to generate a GNU Makefile.
    > For some parts of the file paths I have shortcuts
    > (like $(TOPDIR)) which I'd like to substitute into the
    > paths. I.e. I'd like to go through the hash keys and
    > perform a substitution on them, like in this test case:
    >
    > perl -e '%h=(a=>1,b=>2); for (keys %h) { s/a/b/ }; print keys %h'
    > ab


    I don't see at all how that helps substituting parts of file names
    with makefile variables. A templating solution comes to mind.
    Lot's of modules for that on CPAN, maybe too many.

    > The code above doesn't work as expected - I'd need it to
    > print "bb" and not "ab".


    Do you expect the hash to have the key "b" twice? It can't,
    hash keys are unique.

    > Looks like the hash keys aren't
    > the "lvalue" described in the "perldoc perlsyn":


    [...]

    No, the elements returned from "keys" aren't lvalues. "perldoc
    -f keys" explicitly says so:

    The returned values are copies of the original
    keys in the hash, so modifying them will not
    affect the original hash. Compare "values".

    ("values" *does* return lvalues.)

    > What could I do to solve this problem, please?


    Nothing, as long as your solution requires a hash to contain the
    same key twice. What is it you really want to achieve?

    Anno
    Anno Siegel, Jun 11, 2004
    #3
  4. A. Farber

    Guest

    -berlin.de (Anno Siegel) wrote:
    >
    > No, the elements returned from "keys" aren't lvalues. "perldoc
    > -f keys" explicitly says so:
    >
    > The returned values are copies of the original
    > keys in the hash, so modifying them will not
    > affect the original hash. Compare "values".
    >
    > ("values" *does* return lvalues.)


    I think that they are lvalues, they just aren't lvalues tied
    to the original keys. They are anonymous (if you don't
    count the alias) independent lvalues.

    Xho

    --
    -------------------- http://NewsReader.Com/ --------------------
    Usenet Newsgroup Service $9.95/Month 30GB
    , Jun 11, 2004
    #4
  5. A. Farber

    Anno Siegel Guest

    <> wrote in comp.lang.perl.misc:
    > -berlin.de (Anno Siegel) wrote:
    > >
    > > No, the elements returned from "keys" aren't lvalues. "perldoc
    > > -f keys" explicitly says so:
    > >
    > > The returned values are copies of the original
    > > keys in the hash, so modifying them will not
    > > affect the original hash. Compare "values".
    > >
    > > ("values" *does* return lvalues.)

    >
    > I think that they are lvalues, they just aren't lvalues tied
    > to the original keys. They are anonymous (if you don't
    > count the alias) independent lvalues.


    You are right in that they are assignable (and a good thing that is too).
    What I meant was, of course, "assignable with an effect on the original
    hash".

    Lvalue hash keys, in that sense, would be a bad idea anyway. If a
    key is assigned a string that already exists in the hash, one of the
    corresponding values has to go. It is by no means clear which one,
    but whichever you choose, there is an occasional side effect on the
    hash's values. Not an operation I'd like to see in a language.

    Anno
    Anno Siegel, Jun 11, 2004
    #5
  6. A. Farber

    gnari Guest

    "A. Farber" <> wrote in message
    news:...
    >
    > I have a hash where keys and values are file paths.
    > I'm going to use that hash to generate a GNU Makefile.
    > For some parts of the file paths I have shortcuts
    > (like $(TOPDIR)) which I'd like to substitute into the
    > paths. I.e. I'd like to go through the hash keys and
    > perform a substitution on them, like in this test case:
    >
    > perl -e '%h=(a=>1,b=>2); for (keys %h) { s/a/b/ }; print keys %h'


    perl -e '%h=(a=>1,b=>2); my @k=keys %h; for (@k) { s/a/b/ }; print @k'

    gnari
    gnari, Jun 11, 2004
    #6
  7. A. Farber

    A. Farber Guest

    "gnari" <> wrote in message news:<caddc0$gs7$>...
    > "A. Farber" <> wrote in message
    > news:...
    > >
    > > I have a hash where keys and values are file paths.
    > > I'm going to use that hash to generate a GNU Makefile.
    > > For some parts of the file paths I have shortcuts
    > > (like $(TOPDIR)) which I'd like to substitute into the
    > > paths. I.e. I'd like to go through the hash keys and
    > > perform a substitution on them, like in this test case:
    > >
    > > perl -e '%h=(a=>1,b=>2); for (keys %h) { s/a/b/ }; print keys %h'

    >
    > perl -e '%h=(a=>1,b=>2); my @k=keys %h; for (@k) { s/a/b/ }; print @k'


    Thanks, but that doesn't help me.

    What I have in my program (a converter of some weird build system to
    GNU Makefile) is a hash with files (KEY=destination,VAL=source path):

    $prj_exports = {
    '/mnt/cali5/epoc32/wins/c/nokia/sounds/Digital/Clock_alert.mid'
    => '"../data/Clock alert.mid"',
    '/mnt/cali5/tcf/cmmphonebookstoremesshandler.h' =>
    '../inc/cmmphonebookstoremesshandler.h',
    '/mnt/cali5/tcf/cmmphonebookstoreextinterface.h' =>
    '../inc/cmmphonebookstoreextinterface.h'
    };

    Since I have the value "/mnt/cali5" in my Makefile variable $(TOPDIR)
    already, here is what I'd like to convert the hash above to:

    $prj_exports = {
    '$(TOPDIR)/epoc32/wins/c/nokia/sounds/Digital/Clock_alert.mid'
    => '"../data/Clock alert.mid"',
    '$(TOPDIR)/tcf/cmmphonebookstoremesshandler.h' =>
    '../inc/cmmphonebookstoremesshandler.h',
    '$(TOPDIR)/tcf/cmmphonebookstoreextinterface.h' =>
    '../inc/cmmphonebookstoreextinterface.h'
    };
    A. Farber, Jun 12, 2004
    #7
  8. A. Farber

    Anno Siegel Guest

    A. Farber <> wrote in comp.lang.perl.misc:
    > "gnari" <> wrote in message news:<caddc0$gs7$>...
    > > "A. Farber" <> wrote in message
    > > news:...
    > > >
    > > > I have a hash where keys and values are file paths.
    > > > I'm going to use that hash to generate a GNU Makefile.
    > > > For some parts of the file paths I have shortcuts
    > > > (like $(TOPDIR)) which I'd like to substitute into the
    > > > paths. I.e. I'd like to go through the hash keys and
    > > > perform a substitution on them, like in this test case:
    > > >
    > > > perl -e '%h=(a=>1,b=>2); for (keys %h) { s/a/b/ }; print keys %h'

    > >
    > > perl -e '%h=(a=>1,b=>2); my @k=keys %h; for (@k) { s/a/b/ }; print @k'

    >
    > Thanks, but that doesn't help me.
    >
    > What I have in my program (a converter of some weird build system to
    > GNU Makefile) is a hash with files (KEY=destination,VAL=source path):
    >
    > $prj_Gexports = {
    > '/mnt/cali5/epoc32/wins/c/nokia/sounds/Digital/Clock_alert.mid'
    > => '"../data/Clock alert.mid"',
    > '/mnt/cali5/tcf/cmmphonebookstoremesshandler.h' =>
    > '../inc/cmmphonebookstoremesshandler.h',
    > '/mnt/cali5/tcf/cmmphonebookstoreextinterface.h' =>
    > '../inc/cmmphonebookstoreextinterface.h'
    > };
    >
    > Since I have the value "/mnt/cali5" in my Makefile variable $(TOPDIR)
    > already, here is what I'd like to convert the hash above to:
    >
    > $prj_exports = {
    > '$(TOPDIR)/epoc32/wins/c/nokia/sounds/Digital/Clock_alert.mid'
    > => '"../data/Clock alert.mid"',
    > '$(TOPDIR)/tcf/cmmphonebookstoremesshandler.h' =>
    > '../inc/cmmphonebookstoremesshandler.h',
    > '$(TOPDIR)/tcf/cmmphonebookstoreextinterface.h' =>
    > '../inc/cmmphonebookstoreextinterface.h'
    > };


    So you want do a text replacement to the keys of a hash. The general
    problem with this is the possibility of key clashes. From what you say
    here, clashes can't occur, but that was far from obvious in your
    original posting.

    There is no way of directly replacing a hash key, as has been discussed
    elsewhere in this thread. The general procedure is to delete the old
    key and create the new one (untested):

    $hash{ $newkey} = delete $hash{ $oldkey} if exists $hash{ $oldkey};

    If you want to apply an s///-operation to all keys of the hash, as seems
    to be your case, this is one way:

    for ( keys %hash ) {
    my $oldkey = $_;
    $hash{ $_} = delete $hash{ $oldkey} if s{^/mnt/cali5} {\$(TOPDIR)};
    }

    An existence test is, of course, not needed here. The test of s///
    is there for cleanliness, and, perhaps, efficiency. Without it the
    value would be re-assigned to the unchanged key.

    Anno
    Anno Siegel, Jun 12, 2004
    #8
  9. A. Farber <> wrote:
    > "gnari" <> wrote in message news:<caddc0$gs7$>...
    >> "A. Farber" <> wrote in message
    >> news:...


    >> > I'd like to go through the hash keys and
    >> > perform a substitution on them,



    Which will create a new hash element, you will also need to delete
    the old hash element, and you can't do it while foreach-ing over the hash.

    It would be much better if you could do the s/// while populating
    the hash in the first place, but you haven't shown how you're
    building the hash in your real code...


    >
    > $prj_exports = {
    > '/mnt/cali5/epoc32/wins/c/nokia/sounds/Digital/Clock_alert.mid'
    >=> '"../data/Clock alert.mid"',



    You should have written it with shorter line lengths for posting
    (or at least disabled word-wrapping).



    > here is what I'd like to convert the hash above to:
    >
    > $prj_exports = {
    > '$(TOPDIR)/epoc32/wins/c/nokia/sounds/Digital/Clock_alert.mid'
    >=> '"../data/Clock alert.mid"',




    ----------------------------
    #!/usr/bin/perl
    use warnings;
    use strict;
    use Data::Dumper;

    my $prj_exports = {
    '/mnt/cali5/epoc32/wins/c/nokia/sounds/Digital/Clock_alert.mid'
    => '"../data/Clock alert.mid"',
    '/mnt/cali5/tcf/cmmphonebookstoremesshandler.h'
    => '../inc/cmmphonebookstoremesshandler.h',
    '/mnt/cali5/tcf/cmmphonebookstoreextinterface.h'
    => '../inc/cmmphonebookstoreextinterface.h'
    };

    my $TOPDIR = quotemeta '/mnt/cali5'; # \Q needed if regex metachars
    # might be in the path, though
    # there are none in this example

    my @destinations = keys %$prj_exports; # don't modify hash while foreach-ing
    foreach my $dest ( @destinations ) {
    (my $newdest = $dest ) =~ s/$TOPDIR/\$\(TOPDIR)/; # make new key
    $prj_exports->{$newdest} = $prj_exports->{$dest}; # copy value
    delete $prj_exports->{$dest}; # goodbye old key/value
    }

    print Dumper $prj_exports;
    ----------------------------


    --
    Tad McClellan SGML consulting
    Perl programming
    Fort Worth, Texas
    Tad McClellan, Jun 12, 2004
    #9
  10. A. Farber

    gnari Guest

    "A. Farber" <> wrote in message
    news:...
    > "gnari" <> wrote in message

    news:<caddc0$gs7$>...
    > > "A. Farber" <> wrote in message
    > > news:...
    > > >
    > > > perl -e '%h=(a=>1,b=>2); for (keys %h) { s/a/b/ }; print keys %h'

    > >
    > > perl -e '%h=(a=>1,b=>2); my @k=keys %h; for (@k) { s/a/b/ }; print @k'

    >
    > Thanks, but that doesn't help me.
    >

    [snip example]

    if I understand you correctly, you want

    my @newkeys=@oldkeys=keys %$prj_exports;
    s[^/mnt/cali5/][$(TOPDIR)/] for (@newkeys);
    my $new_export;
    @{$new_export}{@newkeys}=@{$prj_export}{@oldkeys};

    your original post was confusing because your example inplied
    duplicate key 'b' in result.

    gnari
    gnari, Jun 12, 2004
    #10
  11. A. Farber

    A. Farber Guest

    Tad McClellan <> wrote in message
    > >> "A. Farber" <> wrote in message

    >
    > >> > I'd like to go through the hash keys and
    > >> > perform a substitution on them,


    > It would be much better if you could do the s/// while populating
    > the hash in the first place, but you haven't shown how you're
    > building the hash in your real code...


    Thanks, that's what I've ended up with
    A. Farber, Jun 12, 2004
    #11
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. sandeep Kanwal

    serial keys/validation keys

    sandeep Kanwal, Oct 29, 2004, in forum: C++
    Replies:
    1
    Views:
    575
    Mike Wahler
    Oct 29, 2004
  2. Harry George
    Replies:
    9
    Views:
    684
    sonal
    Jun 13, 2006
  3. Replies:
    10
    Views:
    706
    Daniel T.
    Feb 3, 2006
  4. keto
    Replies:
    0
    Views:
    896
  5. David Cournapeau

    print a vs print '%s' % a vs print '%f' a

    David Cournapeau, Dec 30, 2008, in forum: Python
    Replies:
    0
    Views:
    334
    David Cournapeau
    Dec 30, 2008
Loading...

Share This Page