5)[std::cout<

亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

boost 的函數式編程庫 Phoenix入門學習

系統 1964 0

這篇文章是我學習boost phoenix的總結。


序言

Phoenix是一個C++的函數式編程(function programming)庫。Phoenix的函數式編程是構建在函數對象上的。因此,了解Phoenix,必須先從它的基礎函數對象上做起。

Phoenix能夠提供令人驚艷的編碼效果。我先撂一個出來,看看用Phoenix能寫出什么樣的代碼:

?

        std::for_each(vec.begin(), vec.end(),

        if_(arg1 > 5)

        [

            std::cout << arg1 << ">5\n"

        ]

        .else_

        [

            if_(arg1 == 5)

            [

                std::cout << arg1 << "== 5\n"

            ]

            .else_

            [

                std::cout << arg1 << "< 5\n"

            ]

        ]

    );


  

這是C++代碼?答案是肯定的!只需要C++編譯器,不需要任何額外的工具,就能實現這樣的效果。這是怎么回事?且看下面逐步分解。

?


在此之前,編譯phoenix庫必須

包含核心頭文件

?

    #include <boost/phoenix/core.hpp>
  


注意,不要使用using namespace boost::phoenix,而要直接使用using boost::phoenix::val, ....,如

?

?

    using boost::phoenix::val;

using boost::phoenix::arg_names::arg1;

using boost::phoenix::arg_names::arg2;

using boost::phoenix::case_;

using boost::phoenix::ref;

using boost::phoenix::for_;

using boost::phoenix::let;

using boost::phoenix::lambda;

using boost::phoenix::local_names::_a;


  


為什么不要直接使用using namespace boost::phoenix呢?因為這樣會帶來不可預知的問題。這是我在實踐中發現的。boost的宏BOOST_PHOENIX_ADAPT_FUNCTION_NULLARY以及類似的宏,會出現編譯錯誤。真實的原因是什么,沒有細致考究。

?

另外一個原因是,要防止不必要的命名污染,因為phoenix用了很多和boost庫沖突的名稱,這些在使用的時候,很容易造成問題。


基礎函數對象

values

包含頭文件:

?

    #include <boost/phoenix/core.hpp>
  

使用命名空間:

?

?

    using boost::phoenix::val;
  

例子

?

?

    val(3)

val("Hello, World")


  

val(3) 生成一個包含整數3的 函數對象 。val("Hello, World")則是一個包含字符串的 函數對象

?

他們是函數對象,因此,你可以象函數那樣調用他們

?

    std::cout << val(3)() << val("Hello World")()<<std::endl;


  

val(3)() 將返回值3, val("Hello World")() 將返回值"Hello World"。

?

也許,你會覺得,這簡直是多此一舉。但是,事實上,你沒有明白phoenix的真正用以。

val(3)和val("Hello World") 實際上實現了一個懶惰計算的功能,將對3和"Hello World"的求值,放在需要的時候。


上面的表達式,還可以寫成這樣

?

    (std::cout << val(3) << val("Hello World")<<std::endl)();
  

括號中std::cout << .. 這一長串,實際上生成了一個函數對象,因此我們才能在需要的時候,調用這個函數對象。

?


這是val的真正威力,它讓求值推遲到需要的時候。在普通編程中,我們必須通過類和接口才能完成。


?

References

包含頭文件

?

?

    #include <boost/phoenix/core.hpp>
  

使用命名空間

?

?

    using boost::phoenix::ref;


  

如果聲明了如下變量:

?

?

        int i = 3;

    char const* s = "Hello World";

    std::cout << (++ref(i))() << std::endl;

    std::cout << ref(s)() << std::endl;
  


ref與val都是可以延遲求值的,但是,不同的是,ref相當于 int& 和 char const*& 的調用。

?

因此,上面 (++ref(i))()的返回值是4,而且,變量i的值也將變為4.

references是phoenix的函數對象和外部變量交換數據的橋梁。

?

Arguments

還記得boost中有_1, _2, _3, ...這些東西嗎?在phoenix中有一種類似的 arg1, arg2, arg3, ...。他們有相似的作用,但是arg1 事實上是函數對象。

?

包含頭文件

?

    #include <boost/phoenix/core.hpp>
  

?

使用命名空間

?

    using boost::phoenix::arg_names::arg1;

using boost::phoenix::arg_names::arg2;

using boost::phoenix::arg_names::arg3;

....


  

看下面的例子

?

        std::cout << arg1(3) << std::endl;

    std::cout << arg2(2, "hello world") << std::endl;
  


輸出的結果是 3, "Hello world"。?

?

?

  • arg1接收1個以上的參數,然后返回第1個參數
  • arg2接受2個以上的參數,然后返回第2個參數
  • arg3接受3個以上的參數,然后返回第3個參數

?

依次類推。


那么,這樣的東西有什么用呢?它實際上是用來提取參數的。arg1提取第一個參數,arg2提取第二個參數,....

比如,我們有一個函數

?

    void testArg(F f)

{

    f(1,2,3);

}



...

int main()

{

    testArg(std::cout<<arg1<<"-"<<arg2<<"-"<<arg3<<std::endl);

}
  

std::cout ... 這一長串生成了一個函數對象。arg1 ,arg2, arg3分別提取了testArg傳遞的參數1,2,3。因此,這個函數會返回"1-2-3"。如果你將arg1和arg3的位置兌換下,返回的結果將是"3-2-1"。

?


?

Lazy Operators

操作符也可以生成函數對象。

?

頭文件

?

    #include <boost/phoenix/operator.hpp>
  


無需命名空間

?

看個例子

?

    std::find_if(vec.begin(), vec.end(), arg1 %2 == 1);
  


find_if的功能是查找第一個符合條件的對象,然后返回。它要求最后一個參數為一個函數或者函數對象。那么 arg1 %2 == 1是一個函數對象嗎?

?

答案是肯定的!。

它一共涉及兩個操作符 %和 == 。 arg1 % 2 生成一個函數對象,新生成的函數對象在通過 == 操作符,又生成了新的對象。

它實際上就是

?

    auto func1 = operator % (arg1, 2);

auto func2 = operator == (func1, 1);
  

最后的func2被傳遞給了find_if。

?


phoenix支持所有的操作符,包括一元操作符在內,

如:

?

    1 << 3;      // Immediately evaluated

val(1) << 3; // Lazily evaluated
  


支持的單目運算符有

?

?

    
      prefix
    
    
      :
    
    
      ~,
    
    
      !,
    
    
      -,
    
    
      +,
    
    
      ++,
    
    
      --,
    
    
      &
    
    
      (
    
    
      reference
    
    
      ),
    
    
      *
    
    
      (
    
    
      dereference
    
    
      )
    
    
      postfix
    
    
      :
    
    
      ++,
    
    
      --
    
  

?


支持的雙目運算符有

?

    
      =,
    
    
      [],
    
    
      +=,
    
    
      -=,
    
    
      *=,
    
    
      /=,
    
    
      %=,
    
    
      &=,
    
    
      |=,
    
    
      ^=,
    
    
      <<=,
    
    
      >>=
    
    
      +,
    
    
      -,
    
    
      *,
    
    
      /,
    
    
      %,
    
    
      &,
    
    
      |,
    
    
      ^,
    
    
      <<,
    
    
      >>
    
    
      ==,
    
    
      !=,
    
    
      <,
    
    
      >,
    
    
      <=,
    
    
      >=
    
    
      &&,
    
    
      ||,
    
    
      ->*
    
  

?


三目運算符

?

    
      if_else
    
    
      (
    
    
      c
    
    
      ,
    
    
      a
    
    
      ,
    
    
      b
    
    
      )
    
  

?


支持成員函數指針操作

?

    
      struct
    
    
      A
    
    
      {
    
    
      int
    
    
      member
    
    
      ;
    
    
      };
    
    
      A
    
    
      *
    
    
      a
    
    
      =
    
    
      new
    
    
      A
    
    
      ;
    
    
      ...
    
    
      (
    
    
      arg1
    
    
      ->*&
    
    
      A
    
    
      ::
    
    
      member
    
    
      )(
    
    
      a
    
    
      );
    
    
      // returns member a->member
    
  

arg1->*&A::member實現一個對A對象的訪問操作。

?


?

Lazy Statements

懶惰語句。

?

頭文件

?

    #include <boost/phoenix/statement.hpp>
  


命名空間

?

?

    using boost::phoenix::if_;

using boost::phoenix::switch_;

using boost::phoenix::case_;

using boost::phoenix::while_;

using boost::phoenix::for_;

....
  


我們看看if_的例子

?

?

        std::for_each(vec.begin(), vec.end(),

        if_(arg1 > 5)

        [

            std::cout << arg1 << ","

        ]

        );


  


雖然看起來很奇怪,但是,它的確是C++的語法。這個里面也充斥了函數對象。我們可以這樣看

?

?

    if_.operator()( 

     operator > (arg1, 5)

 ) .operator[](

    operator<<(

               operator<<(std::cout, arg1)

             , ",")

)
  

?


Lazy Statement還有很多類似的語法。它的目的是為了模擬C++的語法。 用C++模擬C++


它用逗號代替分號,模擬語句序列,如

?

    
      statement
    
    
      ,
    
    
      statement
    
    
      ,
    
    
      ....
    
    
      statement
    
  

主要,最后一條"語句"(實際上是函數對象),不能有“,”,如是這樣

?

?

    
      statement
    
    
      ,
    
    
      statement
    
    
      ,
    
    
      statement
    
    
      ,
    
    
      // ERROR!
    
  

就錯了。

?

可以用括號來擴住一些語句(即函數對象)

?

    
      statement
    
    
      ,
    
    
      statement
    
    
      ,
    
    
      (
    
    
      statement
    
    
      ,
    
    
      statement
    
    
      ),
    
    
      statement
    
  

括號也可以用在最外層,將語句(即函數對象)進行分組,如

?

?

    
      std
    
    
      ::
    
    
      for_each
    
    
      (
    
    
      c
    
    
      .
    
    
      begin
    
    
      (),
    
    
      c
    
    
      .
    
    
      end
    
    
      (),
    
    
      (
    
    
      do_this
    
    
      (
    
    
      arg1
    
    
      ),
    
    
      do_that
    
    
      (
    
    
      arg1
    
    
      )
    
    
      )
    
    
      );
    
  

?


?

Construct, New, Delete, Casts

可以重載類的這些實現:

?

?

    
      construct
    
    
      <
    
    
      std
    
    
      ::
    
    
      string
    
    
      >(
    
    
      arg1
    
    
      ,
    
    
      arg2
    
    
      )
    
    
      // constructs a std::string from arg1, arg2
    
    
      new_
    
    
      <
    
    
      std
    
    
      ::
    
    
      string
    
    
      >(
    
    
      arg1
    
    
      ,
    
    
      arg2
    
    
      )
    
    
      // makes a new std::string from arg1, arg2
    
    
      delete_
    
    
      (
    
    
      arg1
    
    
      )
    
    
      // deletes arg1 (assumed to be a pointer)
    
    
      static_cast_
    
    
      <
    
    
      int
    
    
      *>(
    
    
      arg1
    
    
      )
    
    
      // static_cast's arg1 to an int*
    
  

?

?

函數適配器

頭文件

?

    #include <boost/phoenix/function.hpp>
  


命名空間

?

?

    boost::phoenix::function
  

?

?

函數對象包裝

?

考慮一個factorial函數

?

    struct factorial_impl

{

    template <typename Sig>

    struct result;

    

    template <typename This, typename Arg>

    struct result<This(Arg)>

        : result<This(Arg const &)>

    {};



    template <typename This, typename Arg>

    struct result<This(Arg &)>

    {

        typedef Arg type;

    };



    template <typename Arg>

    Arg operator()(Arg n) const

    {

        return (n <= 0) ? 1 : n * this->operator()(n-1);

    }

};


  


解析一個這個實現:

?

factorial_impl的result聲明是必須的,這是phoenix的模板要求的。result的聲明使用了半實例化模板

?

        template <typename Sig>

    struct result;
  

這是聲明一個主模板,當然,主模板沒有任何用處,因此只聲明不定義。

?

?

        template <typename This, typename Arg>

    struct result<This(Arg &)>

    {

        typedef Arg type;

    };
  

這是一個半實例化的模板。從 result<This(Arg&)>可以看出。 This(Arg&)聲明一個返回對象為 This, 參數為Arg& 的函數。

?

后面,Arg operator()(Arg)就是函數對象的實現體了。


使用時,需要這樣

?

    int

main()

{

    using boost::phoenix::arg_names::arg1;

    boost::phoenix::function<factorial_impl> factorial;

    int i = 4;

    std::cout << factorial(i)() << std::endl;

    std::cout << factorial(arg1)(i) << std::endl;

    return 0;

}
  

?

?

適配函數宏

?

上面的代碼,書寫起來,還是比較麻煩的,因此,phoniex提供了幾個宏,用于幫助實現函數對象的適配。

針對普通函數的宏

?

BOOST_PHOENIX_ADAPT_FUNCTION_NULLARY BOOST_PHOENIX_ADAPT_FUNCTION

?

它的語法是

?

      
        BOOST_PHOENIX_ADAPT_FUNCTION_NULLARY
      
      
        (
      
      
        RETURN_TYPE
      
      
        ,
      
      
        LAZY_FUNCTION
      
      
        ,
      
      
        FUNCTION
      
      
        )
      
    
      
        BOOST_PHOENIX_ADAPT_FUNCTION
      
      
        (
      
      
        RETURN_TYPE
      
      
        ,
      
      
        LAZY_FUNCTION
      
      
        ,
      
      
        FUNCTION
      
      
        ,
      
      
        FUNCTION_ARITY
      
      
        )
      
    

NULLARY表明是沒有參數的。

?

針對NULLARY的例子:

聲明函數:

?

      namespace demo

{

    int foo()

    {   

        return 42; 

    }   

}
    

生成函數對象

?

?

      BOOST_PHOENIX_ADAPT_FUNCTION_NULLARY(int, foo, demo::foo)


    

使用它:

?

?

      std::cout << "foo()():"<<foo()() << std::endl;


    

foo() 返回一個函數對象。 foo是一個函數,你可以認為是函數對象的工廠。

帶參數的例子

?

?

      namespace demo

{

    int plus(int a, int b)

    {   

        return a+b;

    }   



    template<typename T>

    T plus ( T a, T b, T c)

    {   

        return a + b + c;

    }   

}



BOOST_PHOENIX_ADAPT_FUNCTION(int, myplus, demo::plus, 2)



BOOST_PHOENIX_ADAPT_FUNCTION(

    typename boost::remove_reference<A0>::type

    , myplus

    , demo::plus

    , 3 

    )   


    


這樣使用

?

?

          int a = 123;

    int b = 256;

    std::cout<<"myplus:"<<(myplus(arg1, arg2)(a, b)) << std::endl;

    std::cout<<"myplus<3>:"<<(myplus(arg1, arg2, 3)(a, b)) << std::endl;


    


myplus(arg1, arg2, 3) 生成一個函數對象,這個函數對象接收兩個整數參數。

?


至于細節,了解不是很多,不管怎么樣,用就是了。


針對函數對象的宏

?

BOOST_PHOENIX_ADAPT_CALLABLE_NULLARY BOOST_PHOENIX_ADAPT_CALLABLE

?

在使用上,同FUNCTION對應的函數,但是,它是針對函數對象的。

?

      namespace demo

{

    struct foo2 {

        typedef int result_type;

        int operator()() const

        {

            return 42;

        }

    };

}



BOOST_PHOENIX_ADAPT_CALLABLE_NULLARY(foo2, demo::foo2)
    


聲明方法和BOOST_PHOENIX_ADAPT_FUNCTION_NULLARY 幾乎是一樣的,但是它不需要給出返回值。

?

值得注意的是,foo2中 typedef int result_type; 的聲明是必須的,因為,它是phonix模板要求的,一旦沒有,就會出錯。


帶有重載的例子

?

      namespace demo

{

    struct plus

    {

        template<typename Sig>

        struct result;



        template<typename This, typename A0, typename A1>

        struct result<This(A0, A1)>

            :boost::remove_reference<A0>

            {};

        template<typename This, typename A0, typename A1, typename A2>

        struct result<This(A0, A1,A2)>

            :boost::remove_reference<A0>

            {};



        template<typename A0, typename A1>

? ? ? ? A0 operator()(A0 const& a0, A1 const &a1) const

? ? ? ? {

? ? ? ? ? ? return a0 + a1;

? ? ? ? }

? ? ? ? template<typename A0, typename A1, typename A2>

? ? ? ? A0 operator()(A0 const& a0, A1 const &a1, A2 const &a2) const

? ? ? ? {

? ? ? ? ? ? return a0 + a1 + a2;

? ? ? ? }

? ? };

}



BOOST_PHOENIX_ADAPT_CALLABLE(plus, demo::plus, 2)

BOOST_PHOENIX_ADAPT_CALLABLE(plus, demo::plus, 3)


    

struct result的聲明也是使用了半實例化的技巧。需要給出參數個數,這個是很重要的。

?

語句

語句在上面提到過,這里介紹更多的語句

?

if_else_ 語句

?

我們開頭看到的,就是一個if_else_語句

?

          std::for_each(vec.begin(), vec.end(),

        if_(arg1 > 5)

        [

            std::cout << arg1 << ">5\n"

        ]

        .else_

        [

            if_(arg1 == 5)

            [

                std::cout << arg1 << "== 5\n"

            ]

            .else_

            [

                std::cout << arg1 << "< 5\n"

            ]

        ]

    );
    

?

if_最終生成了一個函數對象,它還有一個.else_對象,這個對象也是一個函數對象,可以接收任何函數對象。于是,這樣就被層層包含起來,形成了上面的奇觀。

?

switch_ 語句

看這個例子
            std::for_each(vec.begin(), vec.end(),

        switch_(arg1)

        [

            case_<1>(std::cout<<arg1<<":"<<val("one") << "\n"),

            case_<2>(std::cout<<arg1<<":"<<val("two") << "\n"),

            default_(std::cout<<arg1<<":"<<val("other value") << "\n")

        ]

    );


      
注意default_后面是不加","的。
case_和default_都是函數對象。

while_ 語句

例子:
              int value;



    std::for_each(vec.begin(), vec.end(),

        (

            ref(value) = arg1,

            while_(ref(value)--)

            [

                std::cout<<ref(value)<<","

            ],

            std::cout << val("\n")

        )

    );


        
我用了ref(value)來作為臨時變量。
這樣的代碼寫起來,和對應的C++代碼很相似。但是它實際上是一堆函數對象的組合。它是延遲加載的,這一點很重要。

與之相似的還有do_while_循環。


for_ 語句

            int iii;

    std::for_each(vec.begin(), vec.end(),

        (

            for_(ref(iii) = 0, ref(iii) < arg1, ++ ref(iii))

            [

                std::cout << arg1 << ", "

            ],

            std::cout << val("\n")

        )

    );


      

無語了。

其他語句

另外還有,try_catch_和throw_語句,實現原理都差不多。

?

總結

以上的介紹是淺嘗輒止,phoenix還有很多高級的東西未曾涉及,有興趣的讀者可以看boost相關內容。

phoenix讓我重新認識了C++的模板。C++的模板是C++元編程的重要利器。它甚至一定程度上改變了C++語言的語法。


不過,個人覺得,phoenix也有些過度設計。其實,語句部分,可以通過編寫專門的函數來實現。這對大多數人來說,也就是多敲幾行代碼的問題。

我覺得有價值的是phoenix對函數的包裝,使得C++的函數具備了懶計算的能力。

懶計算避免了我們定義N多接口,以及和N多接口配合的N^N的類工廠和派生類。

使用FP編程,不必像OO編程那樣,設計者為了保證接口的兼容性,絞盡腦汁的設計接口;使用者不必為了實現一個簡單的功能,派生一大堆類,和一大堆工廠。

設計者根據需要,要求傳遞函數對象即可;使用者只需要包裝一個自己的實現給它使用,一切都搞定了。




?

boost 的函數式編程庫 Phoenix入門學習


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 91成人免费福利网站在线 | 精品动漫中文字幕一区二区三区 | 8848hh四虎免费| 久久免费视频在线观看 | 欧美一级毛片免费观看 | 香蕉久久高清国产精品免费 | 毛片黄片视频 | 日韩在线国产 | 黄色片网站视频 | 日韩欧美一级毛片精品6 | 久久久久久国产精品免费免费 | 99久久影院 | 国产精品午夜久久久久久99热 | 国产福利91精品一区二区三区 | 最新国产三级久久 | 欧美香蕉视频在线观看 | 久久国产精品女 | 国产精品国产三级国产专区5o | 免费在线不卡视频 | 国产亚洲精品久久久久久小说 | 亚洲高清视频在线播放 | 国产日韩一区二区三区在线播放 | 特黄aaaaaaaaa真人毛片 | 色综合久久综合欧美综合网 | 看全色黄大色黄大片爽一下 | 日本一级毛片2021免费 | 天天做天天爱天天爽综合网 | 日本一区二区免费视频 | 羞羞视频在线看 | 久久精品三级视频 | 偷偷鲁影院手机在线观看 | 一本久道久久综合狠狠爱 | 四虎影视精品永久免费网站 | 天天夜碰日日摸日日澡 | 九九婷婷| 爽爽影院在线免费观看 | 狠狠色狠狠色综合网 | 天天舔天天射天天操 | 国产福利不卡视频在免费 | 精品亚洲欧美高清不卡高清 | 成人小片magnet |