将长命令性代码块更改为更具功能性的编程风格

时间:2013-08-20 19:57:55

标签: functional-programming

我是函数式编程的新手,我想学习如何通过编写函数来构建程序(这样我就可以根据自己的需要选择使用单点编程)

我有一长串命令式代码,在伪代码中是这样的:

function do_complicated_stuff(input) {
    //do some DB stuff and get information about A,B,C,D
    //prepare Output A
    //prepare Output B
    //prepare Output C
    //prepare Output D
    if(condition) {
       A = "I am A1"
       B = "I am B1"
       C = "I am C1"
       D = "I am D1"
    } else {
       //do the same as above, but change to I am A2, B2, C2, D2
    }
    array = {A, B, C, D};
    return array;
}

有没有办法把它写成函数应用程序而不是命令式代码?我想要简化的事情:我不想显式处理A,B,C,D所以我不想进行像do_other_stuff这样的函数调用(A,B,C,D,db_stuff_required_for_A,db_stuff_required_for_B)

很明显我希望有一个像do_other_stuff(one_item)这样的函数,但如果one_itemAcondition,则最终返回值应为“我是A1”如果condition为false并且one_itemB,则为“我是B2”。

我知道如何以OOP方式执行此操作,我想我会有一堆对象知道如何处理他们自己的数据,我只需要调用item.other_stuff()然后就可以了解它。我将如何以更具功能性的方式编写相同的东西,组成许多简短的函数,这些函数最终能够为我提供所需的“我是L#”结果?请注意,对于有时重叠的八种情况中的每一种情况,您都需要不同的数据库信息,并且有时每种情况之间完全不同。所以它目前非常复杂而且不是很简单,因为我当前代码中的函数超过100行,并且明确地处理了所有8个案例。更糟糕的是,同样的事情以不同的方式再次完成,其他输出格式如“This is A1”。所以我想将输出与处理和数据库调用分开。假设我可以选择任何语言来重新实现此功能,因此请使用您想要的任何功能。我该怎么做?我需要哪些抽象/语言功能?

支持自己,这是一个实际的PHP代码,它不漂亮,只处理一半的情况(但我想扩展它以涵盖所有内容,当然,而不是使用旧代码)

function do_complicated_stuff(Buysellitem $item) {
    $result = new stdClass();

    global $user_currency;
    $curr_obj = get_cached('getCurrenciesDAO', 'load', array($item->currencyID));
    $price = CurrencyConverter::convert($curr_obj->abbr, $user_currency->abbr, $item->price);
    $dollarPrice = CurrencyConverter::convert($curr_obj->abbr, 'USD', $item->price);

    $item_cat_id = $item->categoryId;
    if(!empty($item_cat_id)) {
        $item_cat = DAOFactory::getCategoryDetailsDAO()->load($item_cat_id)->categoryNameEn;
    }

    $designer_obj = DAOFactory::getUsersDAO()->load($item->userID);

    $item_images = DAOFactory::getBuysellitemimagesDAO()->queryByBuySellID($item->buySellID);
    foreach($item_images as $cur_image) {
        if($cur_image->isDefault) {
            if(!empty($cur_image->aspectRatio) && $cur_image->aspectRatio > 0){
                $main_image_height = round(498 / $cur_image->aspectRatio);
                $main_image =  DOMAIN . SELLIMG_PATH .
                    File_Controller::getImage($cur_image->buySellItemImagePath, 498, $main_image_height);
            }else{
                $main_image = '';
            }
        }
    }

    $designerName = decode_entities($designer_obj->companyName);
    $trim_cat = !empty($item_cat) ? ' #' . strtolower(preg_replace('/\s+/', '', $item_cat)) : '';
    $desc = decode_entities($item->description);
    $trim_desc = str_short($desc, SHARE_LENGTH);
    $item_link = SHARE_ITEM_LINK . $item->buySellID;
    $item_name = str_short(decode_entities($item->buySellName), TW_SHARE);

    if(!empty($price)) {
        $decimals = $price < 10 ? 2 : 0;
        $dollarDecimals = $price < 10 ? 2 : 0;
        $priceFormatted = number_format($price, $decimals, '.', '');
        $dollarPriceFormatted = number_format($dollarPrice, $dollarDecimals, '.', '');

        //if the currency is SEK it doesn't have a sign, so show the abbreviation instead
        $curr_abbr = empty($curr_obj->sign) ? $curr_obj->abbr : '';

        $price_tag = ($item->discount > 0) ?
            ", at {$item->discount}% off":
            " - {$curr_obj->sign}$priceFormatted$curr_abbr";

        $d_price_tag = ($item->discount > 0) ?
            ", at {$item->discount}% off":
            " - \$$dollarPriceFormatted";
    } else {
        $price_tag = '';
        $d_price_tag = '';
    }

    $text = "{$item_name}$price_tag by $designerName";
    $pi_html = "$item_link \n {$item_name}$d_price_tag by $designerName \n $trim_desc";
    $tw_text = "$text$trim_cat #design";
    $tu_html = "<a href='$item_link'><strong>$text</strong></a><br/>$desc";

    $result->facebook = new stdClass();
    $result->twitter = new stdClass();
    $result->tumblr = new stdClass();
    $result->pinterest = new stdClass();

    if(empty($item->video)) {
        $result->facebook->link = FB_LINK . '?' . http_build_query(array(
                'u'=>$item_link,
                'display'=>'popup',
                'redirect_uri'=>FACEBOOK_TRACK
            ), '', '&amp;');
        $result->twitter->link = TW_LINK . '?' . http_build_query(array(
                'original_referrer'=> DOMAIN . $_SERVER['PHP_SELF'],
                'url'=>$item_link,
                'related'=>'CityBlis',
                'via'=>'CityBlis',
                'text'=>$tw_text
            ), '', '&amp;');
        $result->tumblr->link = TU_LINK . '?' . http_build_query(array(
                'source'=>$main_image,
                'caption'=>$tu_html,
                'clickthru'=> $item_link
            ), '', '&amp;');
        $result->pinterest->link = PI_LINK . '?' . http_build_query(array(
                'url'=>$item_link,
                'media'=>$main_image,
                'description'=>$pi_html
            ), '', '&amp;');
    } else {
        $video_link = youtube_vimeo($item->video);

        $result->facebook->link = FB_LINK . '?' . http_build_query(array(
                'link'=>$item_link,
                'display'=>'popup',
                'source'=>$video_link,
                'picture'=>$main_image,
                'redirect_uri'=>FACEBOOK_TRACK
            ), '', '&amp;');
        $result->twitter->link = TW_LINK . '?' . http_build_query(array(
                'original_referrer'=> DOMAIN . $_SERVER['PHP_SELF'],
                'url'=>$item_link,
                'related'=>'CityBlis',
                'via'=>'CityBlis',
                'text'=>$tw_text
            ), '', '&amp;');
        $result->tumblr->link = TU_LINK . '?' . http_build_query(array(
                'embed'=>$video_link,
                'caption'=>$tu_html
            ), '', '&amp;');
        $result->pinterest->link = PI_LINK . '?' . http_build_query(array(
                'url'=>$video_link,
                'media'=>$main_image,
                'description'=>$pi_html,
                'is_video'=>'true'
            ), '', '&amp;');
    }

    return $result;
}

function inline_product_share($input) {

    $item = do_complicated_stuff($input);
    $item->facebook->title = FB_TITLE;
    $item->facebook->innerStyle = '';
    $item->facebook->dimensions = '';
    $item->facebook->follow = '';

    $item->twitter->title = TW_TITLE;
    $item->twitter->innerStyle = '';
    $item->twitter->dimensions = '';
    $item->twitter->follow = '';

    $item->tumblr->title = TU_TITLE;
    $item->tumblr->innerStyle = '';
    $item->tumblr->dimensions = '';
    $item->tumblr->follow = '';

    $item->pinterest->title = PI_TITLE;
    $item->pinterest->innerStyle = '';
    $item->pinterest->dimensions = '';
    $item->pinterest->follow = '';

    return order_output($item);
}


//does the ordering of the output and returns the resultant string
function order_output($item) {
    $style_string = "position:relative;display:inline-block;width:25px;height:25px;
        overflow:hidden;margin-left:4px;vertical-align:top;border-radius:17px;
        background-image: url(\"/i/iSpr.png\");"; //TODO: make it an absolute path when we put it up
    $style = " style='$style_string;";
    $item->facebook->style = $style . "background-color:#3c5897;background-position:-28px 0px;'";
    $item->twitter->style = $style . "background-color:#2daae0;background-position:-55px 0px;'";
    $item->tumblr->style = $style . "background-color:#2a4361;background-position:-82px 0px;'";
    $item->pinterest->style = $style . "background-color:#ca1f25;background-position:-108px 0px;'";

    return  output_item($item->facebook) .
            output_item($item->twitter) .
            output_item($item->tumblr) .
            output_item($item->pinterest);
}

//its only responsibility is to accept an object to return the output html
function output_item($item) {
    ob_start();
?><div<?php echo $item->style?>><a target="_blank" title="<?php
        echo $item->title?>"<?php
        echo $item->innerStyle?> href="<?php
        echo $item->link
        ?>"<?php
        echo $item->follow?><?php
        echo $item->dimensions?>><?php
            echo $item->title?></a></div><?php
    $output = ob_get_contents();
    ob_end_clean();
    return $output;
}

1 个答案:

答案 0 :(得分:0)

我不相信我有足够的信息可以击中头部,因为你没有说A,B,C和D的性质。但是,如果它们像子类一样,我们想要对所有继承的操作执行某种操作,我们有iam函数来对待它们。条件我不知道从哪里来,所以我把它作为一个参数添加到匿名函数索引中,它是从条件中删除的,是从创建函数的范围中关闭的。

function iam(item, index)
{
   return "I am " . item . index; // Might be more advanced than this
}   


function do_complicated_stuff(input, condition) {
    array_of_abcd = db_readabcd(input);

    index = (condition ? 1 : 2);
    handle = function(item)
             {
                 // function has index as free variable
                 return iam( item, index );
             }
    // Use map to iterate over the elelemts of the array
    // applying handle on each one       
    return map(handle array_of_abcd);
}

此示例显示了函数式编程的两个属性。闭包和更高阶函数。 Scheme中也是如此:

(define (iam item index)
  (string-append "I am " 
                 item
                 index))

(define (do-complecated-stuff input condition)

  (let ([array-of-abcd (db-readabcd input)] ; get data
        [index (number->string (if condition 1 2))]) ; get index

    ; use map to iterate over elements with an anonymous function
    (map (lambda (item) (iam item index)) array-of-abcd)))