【问题标题】:Is there a C++ library similar/equivalent to Functional Java?是否有类似于/等效于函数式 Java 的 C++ 库?
【发布时间】:2023-03-08 19:45:01
【问题描述】:

是否有与优秀的Functional Java library 相似或等效的开源 C++ 库?

具体功能包括:

  • 在可迭代对象或类似对象上映射、折叠/减少、过滤等
  • 选项类型
  • 不可变数据结构实现

(出于好奇,已经离开 C++ 好几年了)

是的,传统上认为其中一些功能需要垃圾收集。但是有了现代 C++ 特性和库,是否有人开始通过函数转换或其他方式传递托管指针?

更新 为了清楚起见,我想知道是否有类似于函数式 Java 的东西,因此以下可能是典型的语法:

// assumptions:
//   * my_list is a standard library iterable of ints
//   * f is a function of int that returns a std::string
//   * p is a predicate of std::string returning bool
//   * head_opt returns an option type
stream(my_list).map(f).filter(p).head_opt.get_or_else("None")

这是函数式 Java 提供的习语,相信我很容易习惯它...

【问题讨论】:

  • C++ 有一个垃圾收集器,顺便说一下。不过,这不是标准的一部分。
  • C++ 标准库具有本质上是映射和折叠的功能。他们只是被称为std::transformstd::accumulate。 Boost 有一个选项类型。我不确定您所说的“托管指针”是什么意思
  • @Vlad Lazarenko:是的,你已经证明了我的观点:“C++ 实现能够拥有垃圾收集器”。但 C++ 程序通常不使用垃圾收集器。
  • @ms-ati:是吗?再一次,标准库具有映射/折叠功能(我想std::copy_if 基本上是filter)。语法不同,但这就是函数 所做的
  • @ms-tg:你的问题是你使用了“相似”这个词。因为您的意思不是“相似”; Xeo 给了你一些“类似”的东西。您的意思是“完全相同的语义”。这与“相似”不同。您需要提供功能语义的东西,而不仅仅是功能构造。

标签: c++ boost functional-programming


【解决方案1】:

我认为没有任何库明确具有不可变数据结构。尽管没有人阻止您在某些上下文之外不更改数据结构。

但是你可以从Boost.Range 中构建一些你想要的东西。它具有强大的基于范围的构造、过滤等。不过,您必须自己处理内存管理。


您的问题似乎是,“C++ 中是否有一个库可以完全实现严格的函数式编程结构的行为?”答案是不。据我所知,没有任何 C++ 库以显式和直接实现严格的函数式编程结构为基本目的。 C++ 最终不是一种函数式语言。

各种功能构造有许多近似值。但是没有库可以完全按照严格的函数式编程规则来实现它们。

【讨论】:

  • 我想你误解了什么是不可变数据结构。他们通常能够保留他们的历史。例如,将节点添加到不可变的红/黑树只会更新 log(n) 节点,并且对旧树的任何引用都会使其不可见。
  • @Joel: Err... 添加到一个不可变树?这听起来很不对劲,我认为您应该澄清一下……此外,当某些东西根本不可更改时,就没有“更新”,也就是不可变性的确切含义。
  • 从不可变树中调用 add 函数。我觉得这有点迂腐,所以没有把它写出来。就像 Java 在它们的 immutable 字符串上有一个 + 运算符一样。关键是,尽管似乎返回了一棵新树,但实际上很少出现这种情况。大部分树都被重复使用了。
  • @Xeo 请参阅此链接以了解 Scala 中持久不可变数据结构的解释——这是函数式语言中的一个常见特性:stackoverflow.com/questions/3107151/…
  • @NicolBolas 谢谢!如果“不存在”是正确答案,那么这就是我们想要得到的答案。当然,Java 也不是函数式语言,但经过多次尝试(包括 Guava 等),Functional Java 很好地填补了 Java 世界中的这个漏洞。如果目前不存在用于 C++ 的此类库,那么这就是我正在寻找的答案!再次感谢。
【解决方案2】:

正如@jalf 所说,map 和 fold 已经在标准中,隐藏在不同的名称后面:

  • map -> std::transform,在标题 <algorithm> 中找到
  • 折叠 -> std::accumulate,在标题 <numeric> 中找到

更多功能性的东西可以在Boost.Range 中找到,这是一个非常棒的库。尤其是range adaptors 给人一种真正的功能感觉,因为它们在其他范围内创建视图。使用 C++11,可能的谓词也可以通过 lambda 轻松创建。

Boost.Optional 可能是您的“选项类型”,具体取决于您的确切含义。

C++ 中的不变性可以通过简单地声明您的对象const 来实现。您可以使用按引用参数传递来避免复制。说实话,这当然不等同于真正的函数不变性,因为函数式语言中的不可变容器可以随心所欲地复制,并且通常只是共享内部表示。毕竟,如果你从不写,写时复制是很棒的。

关于您的托管指针,我不知道您所说的它们是什么意思。在 C++ 中,您通常根本不需要指针或动态分配的对象。只需“在堆栈上”创建它们:Foo obj;

如果您指的是共享所有权,则为 std::shared_ptr。如果将这样的指针存储在容器中,甚至还有一个不错的范围适配器:

#include <boost/range/adaptor/indirected.hpp>
#include <boost/range/algorithm/generate.hpp>
#include <boost/range/algorithm/copy.hpp>
#include <vector>
#include <memory>
#include <algorithm>
#include <iterator>
#include <iostream>

int main(){
  std::vector<std::shared_ptr<int>> v(5);
  int i = 0;
  boost::generate(v, [&i]{ return std::make_shared<int>(i++); });
  boost::copy(v | boost::adaptors::indirected,
      std::ostream_iterator<int>(std::cout));
}

你的具体例子

my_list.map(f).filter(p).head_opt.get_or_else("not found")

可以这样实现(注意std::vector是C++中的默认容器):

// Warning, C++11 only!
// Boost.Range doesn't like lambdas without this:
#define BOOST_RESULT_OF_USE_DECLTYPE

#include <vector>
#include <string>
#include <iterator>
#include <iostream>
#include <boost/optional.hpp>
#include <boost/range/adaptor/filtered.hpp>
#include <boost/range/adaptor/transformed.hpp>
#include <boost/range/algorithm/generate.hpp> // only needed for filling the vector
#include <boost/range/algorithm/copy.hpp> // only needed for printing

// we need a little helper for the optional stuff
struct head_opt_gen{} head_opt; // just a tag type

template<class Range>
auto operator|(Range const& r, head_opt_gen)
  -> boost::optional<decltype(r.front())>
{
  if(r.empty())
    return boost::none;
  return r.front();
}

int main(){
  using namespace boost::adaptors;
  std::vector<int> v(5);
  int i = 0;
  boost::generate(v, [&]()->int{ ++i; return i*i; });
  // first, without the optional stuff
  boost::copy(v | transformed([](int x){ return std::to_string(x); })
                | filtered([](std::string const& s){ return s.size() > 1; }),
      std::ostream_iterator<std::string>(std::cout, "\n"));
  std::cout << "=====================\n";
  // now with
  std::cout << boost::get_optional_value_or(
      v | transformed([](int x){ return std::to_string(x); })
        | filtered([](std::string const& s){ return s.size() > 2; }) // note: > 2
        | head_opt, "none");
}

使用 Clang 3.1 Trunk 编译,结果如下:

16
25
=====================
none

【讨论】:

  • @ms-tg:很抱歉,我们会尽我们所能回答问题。如果您对我们的回答不满意,那么合乎逻辑的结论是您的问题应该改进,而不是“所有花时间尝试的人i> 并回答我的问题应该学习 4 种新语言,然后给我写一个答案
  • 最后,除此之外,我确实了解了一些函数式语言。正如你所说,我知道 map/reduce 是什么,是的,在我的 cmets 中,我暗示了如何以命令式风格做同样的事情,因为当我阅读这个问题时,这是我能想到的最好的建议。底线是 *C++ 不是函数式语言。它可以近似,@Xeo 已经展示了 *one 这种可能的近似。如果你想要一个不同的近似值,那么写一个更好的问题,它确切地指定答案应该提供什么。 “类似其他语言/库的东西”非常模糊。
  • @ms-tg:您确实指定了接口,Xeo 为您提供了该接口的一个很好的近似值。不,这不是一字不差的相同。但这或多或少是您要求能够做到的。
  • @ms-tg:据我所知,boost::optional 正是维基百科“选项类型”文章中描述的内容。它不包含任何内容或包含一个值,get_optional_value_or 正是您的get_or_else。另外,我不得不承认我只是从远处看函数式语言,但是“命令式风格”到底是什么意思? vector的填充?请举一个具体的例子。
  • @ms-tg: Here 是从可选绑定示例到 C++ 的 1:1 转换。请注意,我需要自己编写“bind”(坏名,apply 会更好)和打印功能,因为boost::optional 不提供开箱即用的功能。但是,我什至不需要指定我想要打印的确切内容。 :P 请注意,理论上您可以“范围化”boost::optional,只需在 1 个元素范围内使用 Boost.Range 适配器,这样您就不必自己编写 transform/filter/etc。
【解决方案3】:

FC++ 似乎是一个较旧的库(2001 年,last modified in 2007 on SourceForge),在 C++ 中提供了一些函数式编程特性。

嗯,FC++ was submitted as a potential Boost library in 2003?

StackOverflow 上的其他地方,the primary original developer of FC++ indicated that modern C++ and Boost have superceded some of FC++'s use cases, but that others are still not available in a modern C++ library?

似乎有人写了README for a github project,基本上准确地描述了我的要求,但似乎没有进一步了解该项目。

希望这对某人有所帮助...

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-20
    • 1970-01-01
    • 1970-01-01
    • 2014-01-28
    • 2010-09-08
    相关资源
    最近更新 更多