簡體   English   中英

如何在需要兩個參數的函數的向量上使用 std::ranges?

[英]How to use std::ranges on a vector for a function that needs two arguments?

我一直在嘗試了解新的范圍庫,並嘗試將一些更傳統的 for 循環轉換為功能代碼。 cppreference給出的示例代碼非常簡單易讀。 但是,我不確定如何在需要查看、計算和比較每個 x 和 y 值的點向量上應用范圍,最后比較哪個是最大距離。

struct Point
{
  double x;
  double y;
}

double ComputeDistance(const Point& p1, const Point& p2)
{
  return std::hypot(p1.x - p2.x, p1.y - p2.y);
}

double GetMaxDistance(const std::vector<Point>& points)
{
  double maxDistance = 0.0;

  for (int i = 0; i < points.size(); ++i)
  {
    for(int j = i; j < points.size(); ++j)
    {
      maxDistance = std::max(maxDistance, ComputeDistance(points.at(i),points.at(j)));
    }
  }
  return maxDistance;
}

GetMaxDistance是我想嘗試清理並在其上應用范圍的代碼。 我認為這就像做類似的事情一樣簡單:

double GetMaxDistance(const std::vector<Point>& points)
{
  auto result = points | std::views::tranform(ComputeDistance);
  return static_cast<double>(result);
}

然后我意識到這是不正確的,因為我沒有將任何值傳遞給函數。 所以我認為:

double GetMaxDistance(const std::vector<Point>& points)
{
  for(auto point : points | std::views::transform(ComputeDistance)) 
    // get the max distance somehow and return it?
    // Do I add another for(auto nextPoint : points) here and drop the first item?
}

但是后來我意識到我正在將該函數應用於每個點,而不是它旁邊的點,這也行不通,因為我仍然只將一個參數傳遞給函數ComputeDistance 由於我需要計算向量中所有點的距離,我必須將每個點相互比較並進行計算。 將其保留為n^2算法。 我不是想打敗n^2 ,我只是想知道是否有辦法讓這種傳統的 for 循環采用現代的功能性方法。

這讓我們回到了標題。 在這種情況下如何應用std::ranges 甚至有可能與標准在這一點上給我們的東西有關嗎? 我知道更多內容將在 C++23 中添加。 所以我不知道這在發布之前是否無法實現,或者這是否根本不可能。

謝謝!

您正在尋找的算法是組合 - 但沒有范圍適配器(在 C++20 和 range-v3 中都沒有,在 C++23 中也沒有)。

但是,在這種情況下,我們可以使用通常稱為 flat-map 的算法手動構建它:

inline constexpr auto flat_map = [](auto f){
    return std::views::transform(f) | std::views::join;
};

我們可以使用如下:

double GetMaxDistance(const std::vector<Point>& points)
{
    namespace rv = std::views;
    return std::ranges::max(
        rv::iota(0u, points.size())
        | flat_map([&](size_t i){
            return rv::iota(i+1, points.size())
                 | rv::transform([&](size_t j){
                     return ComputeDistance(points[i], points[j]);
                 });
          }));
}

外部iota是我們的第一個循環。 然后對於每個i ,我們從i+1開始得到一個序列來得到我們的j 然后對於每個(i,j)我們計算ComputeDistance

或者,如果您希望在頂層進行transform (可以說更干凈):

double GetMaxDistance(const std::vector<Point>& points)
{
    namespace rv = std::views;
    return std::ranges::max(
        rv::iota(0u, points.size())
        | flat_map([&](size_t i){
            return rv::iota(i+1, points.size())
                 | rv::transform([&](size_t j){
                     return std::pair(i, j);
                 });
          })
        | rv::transform([&](auto p){
            return ComputeDistance(points[p.first], points[p.second]);
          }));
}

甚至(此版本產生一系列對Point的引用,以允許更直接的transform ):

double GetMaxDistance(const std::vector<Point>& points)
{
    namespace rv = std::views;
    namespace hof = boost::hof;

    return std::ranges::max(
        rv::iota(0u, points.size())
        | flat_map([&](size_t i){
            return rv::iota(i+1, points.size())
                 | rv::transform([&](size_t j){
                     return std::make_pair(
                         std::ref(points[i]),
                         std::ref(points[j]));
                 });
          })
        | rv::transform(hof::unpack(ComputeDistance)));
}

這些基本上都做同樣的事情,只是在哪里以及如何調用ComputeDistance函數的問題。


C++23 將添加cartesian_productchunk (range-v3 現在有它們) ,並且最近添加了zip_transform ,這也將允許:

double GetMaxDistance(const std::vector<Point>& points)
{
    namespace rv = std::views;
    namespace hof = boost::hof;

    return std::ranges::max(
        rv::zip_transform(
           rv::drop,
           rv::cartesian_product(points, points)
           | rv::chunk(points.size()),
           rv::iota(1))
        | rv::join
        | rv::transform(hof::unpack(ComputeDistance))
    );
}

cartesian_product本身會給你所有對 - 其中包括(x, x)所有x(x, y)(y, x) ,你都不想要。 當我們通過points.size()對它進行points.size()塊(產生長度為N N范圍)時,我們會反復刪除逐漸增加的( iota(1) )數量的元素......所以只有第一個塊中的一個(包含第一個元素兩次),然后是第二個塊中的兩個( (points[1], points[0])(points[1], points[1])元素),等等。

zip_transform部分仍然產生一系列Point對的塊, join將其減少為一系列Point對,然后我們需要將其unpackComputeDistance

這一切都存在於 range-v3 中(除了zip_transform被命名為zip_with )。 不過,在 range-v3 中,你會得到common_tuple ,Boost.HOF 不支持,但你可以讓它工作

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM