用新向量中对应的值替换向量中所有奇数值

7

给定以下向量:

std::vector<int> foo{1,2,3,4,5,6,7};
std::vector<int> bar{10,20,30,40,50,60,70};

最终我希望foo包含值{ 10, 2, 30, 4, 50, 6, 70 },表示替换所有奇数值。
我尝试了std::replace_if算法,但如何访问相应的值?
// replace_copy_if example
#include <iostream>     // std::cout
#include <algorithm>    // std::replace_copy_if
#include <vector>       // std::vector

bool IsOdd (int i) { return ((i%2)==1); }

int main () {
  std::vector<int> foo{1,2,3,4,5,6,7};
  std::vector<int> bar{10,20,30,40,50,60,70};

  std::replace_if (foo.begin(), foo.end(), IsOdd, 0);

  std::cout << "foo contains:";
  for (auto i: foo){ std::cout << ' ' << i; }
  std::cout << '\n';

  return 0;
}

// output         : foo contains: 0 2 0 4 0 6 0
// desired output : foo contains: 10 2 30 4 50 6 70

如果两个数组的大小不同怎么办? - User_67128
好的观点。在我的特定情况下,它会事先进行检查,因为数据可能会损坏。 - Lumpi
所以你想要替换 foo 中的值,但是你测试打印的是 bar,这是怎么回事? - anastaciu
把代码修正了,以使其与描述相符。 - Lumpi
5个回答

8
您可以使用接受两个输入迭代器范围的 std::transform 重载函数。
std::transform(foo.begin(), foo.end(), bar.begin(), foo.begin(),
  [](auto const& a, auto const& b) {
     if (a % 2)
        return b;
     return a; 
  }
);

1
三元运算符可以使 lambda 函数的代码更简洁、更清晰,我的看法是:返回 a%2 ? b : a。 - Uri Raz
1
@UriRaz 是的,为了简洁起见,我确实考虑过使用三元运算符(即,在lambda表达式的主体中使用return (a%2)? b: a;,一个单独的return语句)。但是,我认为这可能会影响可读性。 - JFMR

2

除了这里的答案,您还可以自己编写一个函数:

演示:https://godbolt.org/z/yf3jYx

void IsOdd (const std::vector<int>& a, std::vector<int>& b) { 
    for(size_t i = 0; i < a.size() && i < b.size(); i++){
        if(b[i] % 2 == 1)
            b[i] = a[i];
    }
}

并在主函数中调用它:

IsOdd(bar, foo);

问题是 - “最终我希望“foo”包含值{10, 2, 30, 4, 50, 6, 70},意味着替换所有奇数值。”你用“a”替换“b”的意思是将“bar”替换为“foo”。 - User_67128
@ManojBanik,确实是这样,打印的向量是bar让我感到困惑。 - anastaciu
@ManojBanik,当你指出我的错误时,我已经解决了它,只是忘记更改输出文本。感谢您调试我的代码,;) - anastaciu

1
你可以利用 std::transform
std::vector<int> result;

std::transform(
    foo.begin(), foo.end(), bar.begin(),
    std::back_inserter(result),
    [](const auto& a, const auto& b) {
        if (a % 2 == 0) {
            return a;
        }
        return b;
    }
);

演示


1
// replace_copy_if example
#include <iostream>     // std::cout
#include <vector>       // std::vector

int main () {
  std::vector<int> foo{1,2,3,4,5,6,7};
  std::vector<int> bar{10,20,30,40,50,60,70};

  for (size_t i = 0; i < bar.size(); i++)
  {
     if (foo[i]%2==1) foo[i]=bar[i];
  }


  std::cout << "bar contains:";
  for (auto i: foo){ std::cout << ' ' << i; }
  std::cout << '\n';

  return 0;
}

anastaciu,这段代码怎么会替换偶数?你看过问题描述吗?@aleem md,你可以使用“if (foo[i]%2)”代替“if (foo[i]%2==1)”。在开始时应该检查两个数组的长度,否则可能会导致意外的结果。 - User_67128

1

std::replace_if只支持用作最后一个参数的常量值替换元素。但是,如果您觉得这对您有意义,可以根据参考实现编写自己的replace_if函数,并使其接受某种生成器作为最后一个参数。例如:

template<class ForwardIt, class UnaryPredicate, class Generator>
void my_replace_if(ForwardIt first, ForwardIt last,
                UnaryPredicate p, Generator g)
{
    for (; first != last; ++first) {
        if(p(*first)) {
            *first = g();
        }
    }
}

bool IsOdd (int i) { return ((i%2)==1); }

int main(int argc, const char * argv[]) {
    std::vector<int> foo{1,2,3,4,5,6,7};
    std::vector<int> bar{10,20,30,40,50,60,70};
    int i = 0;
    my_replace_if (foo.begin(), foo.end(), IsOdd, [&bar, &i]()
        {
            return bar[i++];
        });
    for (auto el : foo)
    {
        std::cout << el << std::endl;
    }
    return 0;
}

===== 编辑 =====

正如anastaciu所指出的那样,生成器的索引应该在每一步中递增,而不仅仅在调用生成器时。

e.g.:

    my_replace_if (foo.begin(), foo.end(),
        [&i](int v) { i++; return ((v%2)==1); },
        [&bar, &i]() { return bar[i-1]; });

但说实话,在这种情况下这确实有些过度,而且看起来也不好看。

1
这将输出 10 2 20 4 30 6 40 - anastaciu
喜欢那种方法,但对于我的特定情况来说有些过度,因为它只被使用了一次。 - Lumpi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接