Perl-Python-a-Day: one-liner loop Functional Style

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Xah Lee

    #1

    Perl-Python-a-Day: one-liner loop Functional Style

    One-Liner Loop in Functional Style

    Xah Lee, 200510

    Today we show a example of a loop done as a one-liner of Functional
    Programing style.

    Suppose you have a list of file full paths of images:

    /Users/t/t4/oh/DSCN2059m-s.jpg
    /Users/t/t4/oh/DSCN2062m-s.jpg
    /Users/t/t4/oh/DSCN2097m-s.jpg
    /Users/t/t4/oh/DSCN2099m-s.jpg
    /Users/t/Icons_dir/icon_sum.gif

    For those ending with -s (indicate a smaller version), you want to
    change it to the full version without the -s, if it exists. Here's the
    code:

    # -*- coding: utf-8 -*-
    # python

    import re, os.path

    imgPaths=[u'/Users/t/web/Periodic_dosage _dir/lanci/t4/oh/DSCN2059m-s.jpg',
    u'/Users/t/web/Periodic_dosage _dir/lanci/t4/oh/DSCN2062m-s.jpg',
    u'/Users/t/web/Periodic_dosage _dir/lanci/t4/oh/DSCN2097m-s.jpg',
    u'/Users/t/web/Periodic_dosage _dir/lanci/t4/oh/DSCN2099m-s.jpg',
    u'/Users/t/web/Icons_dir/icon_sum.gif']


    # change the image path to the full sized image, if it exists
    # that is, if image ends in -s.jpg, find one without the '-s'.
    imgPaths2=[]
    for myPath in imgPaths:
    p=myPath
    (dirName, fileName) = os.path.split(m yPath)
    (fileBaseName, fileExtension)= os.path.splitex t(fileName)
    if(fileBaseName[-2:] == '-s'):
    p2=os.path.join (dirName,fileBa seName[0:-2]) + fileExtension
    if os.path.exists( p2): p=p2
    imgPaths2.appen d(p)

    print imgPaths2

    But how do you do it in a functional programing style? Namely,
    something of the from imgPath2=f(imgP ath), where the f is some
    function. Normally, the f would be a pure function construct made up on
    the spot, that is, lambda. But Python's lambda is limited to only a
    expression as its body. Nevertheless, one can achieve a functional
    style with some workaround, using map twice. Here's the code:

    imgPaths3 = map( lambda x: os.path.exists( x[1]) and x[1] or x[0], \
    map(lambda x: (x, re.sub( r"^(.+?)-s(\.[^.]+)$",r"\1\2", x)),
    imgPaths))

    The first map:

    newList = map(lambda x: (x, re.sub( r"^(.+?)-s(\.[^.]+)$",r"\1\2", x)),
    imgPaths)

    generate a list of pairs (x, y), where x is the same element in
    imgPaths, and y is one without the -s. Then, a second map:

    map( lambda x: os.path.exists( x[1]) and x[1] or x[0], newList,
    imgPaths))

    checks if the path y exists, if so, use that, else, use the x part. The
    function body is essentially of a conditional EXPRESSION, of the from
    (test, if true return result1, else return result2). This form of a
    test that returns a expression is very important in functional
    programing. Because, in functional programing a common pattern is to
    sequence functions and passing values. One cannot stop in the middle
    and use a block structure. Here's how this form's syntax in several
    languages:

    test? trueExpression: falseExpression (C, Perl)
    If[test, trueExpression, falseExpression] (Mathematica)
    (test trueExpression falseExpression ) (LISP)

    In Python, there's no such form for this, but a semantically equivalent
    workaround is to sequence boolean expressions as used in our example.
    Namely:

    test and trueExpression or falseExpression (Python)

    This works because it exploits a particular behavior of how Python
    treats boolean sequences. In Python, “x and y” returns y ifx is
    true. And, “x or y” returns x if x is true. This behavior is
    compatible with the mathematical sense of booleans. For example,
    mathematically “x and y” should be true if both are true, yet in
    Python if x is true then y is returned, and if y is true then this is
    compatible with the math sense, but if y is false then the whole result
    is also false, so it also satisfies the math sense. Similar is the case
    of “x or y”. The point here is that one of the element is returned,
    instead of a real True or False value. Therefore, in a twisted way this
    can be used for the If[test, trueExpression, falseExpression] form.
    Example.

    result1= True and 4 or 5 # returns 4
    result2= False and 4 or 5 # returns 5

    print result1
    print result2

    Such language behavior is a result of the laziness of the compiler
    implementation, particular seen in unix shells (&& and ||). The problem
    with this design is that codes relying on the the returned element of a
    boolean sequence does not clearly indicate the programer's intention.
    It works by language pecularities, instead of expressed program logic.

    For the official doc on evaluating booleans, see:
    The official home of the Python Programming Language


    Here's the Perl code of the same loop block:

    # perl
    use File::Basename;

    @imgPaths=(
    '/Users/t/web/Periodic_dosage _dir/lanci/t4/oh/DSCN2059m-s.jpg',
    '/Users/t/web/Periodic_dosage _dir/lanci/t4/oh/DSCN2062m-s.jpg',
    '/Users/t/web/Periodic_dosage _dir/lanci/t4/oh/DSCN2097m-s.jpg',
    '/Users/t/web/Periodic_dosage _dir/lanci/t4/oh/DSCN2099m-s.jpg',
    '/Users/t/web/Icons_dir/icon_sum.gif');

    # change the image path to the full sized image, if it exists
    # that is, if image ends in -s.jpg, find one without the '-s'.
    $imgPaths2=();
    for $myPath (@imgPaths){
    $p=$myPath;
    ($fileBaseName, $dirName, $fileExtension) = fileparse($myPa th,
    ('\.[^.]+$') );
    if (substr($fileBa seName,-2,2) eq '-s') {
    $p2= $dirName . '/' .
    substr($fileBas eName,0,length( $fileBaseName)-2) . $fileExtension;
    print $p2, "\n";
    if (-e $p2) { $p=$p2}
    }
    push(@imgPaths2 ,$p);
    }

    use Data::Dumper;
    print Dumper(\@imgPat hs2)

    In Perl, this can be written in a functional style one-liner:

    @imgPaths3= map{
    $y = $_;
    $y =~ s/^(.+?)-s(\.[^.]+)$/$1$2/;
    -e $y ? $y : $_;
    } @imgPaths;

    For the official doc of Perl's map, type in command line: “perldoc -f
    map”
    ------
    this article is archived at:


    Xah
    xah@xahlee.org
    ∑ http://xahlee.org/

Working...