2011-09-03 87 views
12

考慮下面的玩具代碼,以確定範圍是否包含一個元素:如何從lambda函數中返回?

template<typename Iter, typename T> 
bool contains1(Iter begin, Iter end, const T& x) 
{ 
    for (; begin != end; ++begin) 
    { 
     if (*begin == x) return true; 
    } 
    return false; 
} 

(是的,我知道,有標準庫已經完全正常的算法,這不是問題的關鍵。)

如何我會寫for_each和lambda?以下不起作用...

template<typename Iter, typename T> 
bool contains2(Iter begin, Iter end, const T& x) 
{ 
    std::for_each(begin, end, [&x](const T& y) { 
     if (x == y) return true; 
    }); 
    return false; 
} 

...因爲那隻會從lambda返回,而不是從函數返回。

我必須拋出異常才能擺脫lambda?同樣,對於這個不涉及lambda的特定問題,可能有十多個更好的解決方案,但這並不是我所要求的。

+1

你不能從lambda這樣返回。對編譯器來說,Lambda是另一個函數,可以在其他地方傳遞。將lambda傳遞給另一個方法會非常愚蠢,在這種方法中,調用會跳到2個級別,不是嗎? – nothrow

+3

如果你不想處理所有的元素,你真的不應該使用for_each。 –

+1

你不能這樣做。您可以通過許多其他方式實現相同的效果。你有沒有一個沒有人爲的例子,它實際上是值得的? – Mankarse

回答

7

我該怎麼寫for_each和lambda?

您不能(不考慮例外情況)。你的函數不是和for-each循環同構的(=一種映射),它就像那樣簡單。

相反,您的功能是通過縮小來描述的,所以如果您想使用高階函數來替換它,請使用縮減而不是地圖。

如果 C++ 合適,通用reduce那麼你的算法將如下所示:

template<typename Iter, typename T> 
bool contains2(Iter begin, Iter end, const T& x) 
{ 
    return stdx::reduce(begin, end, [&x](const T& y, bool accumulator) { 
     return accumulator or x == y; 
    }); 
} 

當然,這只是退出早在壓下正確專門用於布爾結果值,以便短路。

唉,C++沒有提供這樣的功能,據我所知。有accumulate但不會短路(它不能 - C++不知道操作裏面的lambda是短路的,它不是遞歸實現的)。

+0

如果您希望通用的reduce實現短路,則需要使用惰性類型 – ysdx

+0

@ysdx你可以專門研究函數的特徵('is_short_circuited'),不是很優雅,而是一般用途。 –

0

在這種情況下,lambda就像從給定函數contains2()調用的任何其他函數一樣。從其他函數返回並不意味着你從給定函數返回。因此,這是不可能這就是設計應該如何去。

對於像給定例子那樣的模式,拋出異常是不必要的開銷。我會在lambda表中設置一個bool變量,而不是return(也可以設置begin = end;)。這個bool可以檢查從給定函數contains2()返回。

+0

除了設置布爾意味着其餘的序列將被迭代,而拋出異常將結束迭代。根據序列的大小,拋出一個異常可能會更快(然後再次,我懷疑性能問題,因爲這顯然只是一個玩具「假設」的例子。) – jalf

+0

@jalf,我們也可以設置'begin = end ;'在lambda函數內部避免重複序列的其餘部分 – iammilind

+0

你可以嘗試,但是假設'for_each'不會在內部複製這些迭代器 – jalf

7

std::for_each如果您想盡早結束循環,則不是您應該使用的算法。看來你想要std::find_if或類似的東西。您應該使用最適合您的任務的算法,而不僅僅是您熟悉的算法。


如果你真的,真的真的必須從算法早期的 「迴歸」,你能夠 -

警告:接踵而來的是一個真正的,實在壞主意和你幾乎不應該這樣做。確實,看代碼可能會讓你的臉變得模糊。 你已被警告!

拋出一個異常:

bool contains2(Iter begin, Iter end, const T& x) 
{ 
    try { 
    std::for_each(begin, end, [&x](const T& y) { 
     if (x == y) 
      throw std::runtime_error("something"); 
    }); 
    } 
    catch(std::runtime_error &e) { 
    return true; 
    } 
    return false; 
} 
+6

你基本上只是在沒有問號的情況下重新解釋這個問題,他* *他知道有更合適的'std'算法,並且他提到了拋出異常的可能性。 – jalf

+1

-1什麼是jalf表示 – IronMensan

2

Lambda表達式是錯誤的抽象級別,因爲他們的行爲在很大程度上類似的功能 - 至少當它涉及到控制流,這是這裏最重要的。你不想把某些東西當作一個函數(或程序編程的程序)「封裝」,它只能在C++中直接返回或拋出異常。任何顛覆這種行爲的企圖都應該被認爲是病態的 - 或者至少不應該僞裝成一種程序。

對於執行流程的細粒度控制,類似於協程的東西可能是更適合的抽象級別和/或基元。儘管如此,恐怕最終的結果看起來不會像使用std::for_each

2

使用自定義算法:

template<class I, class F> 
bool aborting_foreach(I first, I last, F f) { 
    while(;first!=last;++first) { 
    if(!f(*first)) 
     return false;  
    } 
    return true; 
} 

好吧,其實這是性病:: all_of但你的想法。 (請參閱「縮小答案」)。如果你的函數需要返回某些類型的,你可能想要使用一些變量類型:

// Optional A value 
template<class A> 
class maybe { 
    // ... 
}; 

// Stores either a A result of a B "non local return" 
template<class A, class B> 
class either { 
    … 
}; 

請參見相應的哈斯克爾類型。你可以使用C++ 01「unrestricted union」來實現這一點。

乾淨的方式做非本地退出,使用continuations,但你沒有他們在C + +。

0

正如你和其他人指出的for_each不是在這裏使用的正確算法。沒有辦法擺脫for_each循環 - 除了例外(雙關意圖) - 你必須完全運行它。

template<typename Iter, typename T> 
bool contains2(Iter begin, Iter end, const T& x) 
{ 
    bool tContains = false; 
    std::for_each(begin, end, [&](const T& y) mutable { 
     tContains = tContains || x == y; 
    }); 
    return tContains; 
} 
1

使用std::any_of

template<typename Iter, typename T> 
bool contains2(Iter begin, Iter end, const T& x) 
{ 
    const bool contains = std::any_of(begin, end, [&x](const T& y) 
    { 
     return x == y; 
    }); 

    return contains; 
}