gpt4 book ai didi

c++ - Haskell 风格 "Maybe"类型和 C++11 中的 *chaining*

转载 作者:IT老高 更新时间:2023-10-28 12:43:47 26 4
gpt4 key购买 nike

在我的工作项目中,我反复发现自己需要 Haskell 风格的 Maybe(尤其是 Maybe 链接)。例如。来自客户的提款请求,我们得到客户 ID...在缓存中查找客户...如果找到客户...查找她的储蓄账户...如果有账户...提款...随时在这个链中,如果查找失败,什么都不做,返回一个失败。

我的链很大...有时长达 6...所以这是我在 C++0x 中对 Haskell.Data.Maybe 的滑动...(注意...这个如果我停止使用可变参数模板,应该在 C++ 中工作)。我已经为带一个参数的自由函数或不带参数的成员函数制定了链接,我对接口(interface)很满意。但是,对于采用多个参数的函数......我必须编写一个 lambda 函数来模拟部分应用程序。有没有办法避免它?请参见 main() 的最后一行。即使未注释它也不会编译,而是用于 const/non-const 混合。但问题仍然存在。

对大量代码感到抱歉...我希望这不会拒绝那些可能对此感兴趣的人...

#include <iostream>
#include <map>
#include <deque>
#include <algorithm>
#include <type_traits>

typedef long long int int64;

namespace monad { namespace maybe {

struct Nothing {};

template < typename T >
struct Maybe {
template < typename U, typename Enable = void >
struct ValueType {
typedef U * const type;
};

template < typename U >
struct ValueType < U, typename std::enable_if < std::is_reference < U >::value >::type > {
typedef typename std::remove_reference < T >::type * const type;
};

typedef typename ValueType < T >::type value_type;

value_type m_v;

Maybe(Nothing const &) : m_v(0) {}

struct Just {
value_type m_v;
Just() = delete;
explicit Just(T &v) : m_v(&v) {
}
};

Maybe(Just const &just) : m_v(just.m_v) {
}
};

Nothing nothing() {
return Nothing();
}

template < typename T >
Maybe < T > just(T &v) {
return typename Maybe < T >::Just(v);
}

template < typename T >
Maybe < T const > just(T const &v) {
return typename Maybe < T const >::Just(v);
}

template < typename T, typename R, typename A >
Maybe < R > operator | (Maybe < T > const &t, R (*f)(A const &)) {
if (t.m_v)
return just < R >(f(*t.m_v));
else
return nothing();
}

template < typename T, typename R, typename A >
Maybe < R > operator | (Maybe < T > const &t, Maybe < R > (*f)(A const &)) {
if (t.m_v)
return f(*t.m_v);
else
return nothing();
}

template < typename T, typename R, typename A >
Maybe < R > operator | (Maybe < T > const &t, R (*f)(A &)) {
if (t.m_v)
return just < R >(f(*t.m_v));
else
return nothing();
}

template < typename T, typename R, typename A >
Maybe < R > operator | (Maybe < T > const &t, Maybe < R > (*f)(A &)) {
if (t.m_v)
return f(*t.m_v);
else
return nothing();
}

template < typename T, typename R, typename... A >
Maybe < R > operator | (Maybe < T const > const &t, R (T::*f)(A const &...) const) {
if (t.m_v)
return just < R >(((*t.m_v).*f)());
else
return nothing();
}

template < typename T, typename R, typename... A >
Maybe < R > operator | (Maybe < T const > const &t, Maybe < R > (T::*f)(A const &...) const) {
if (t.m_v)
return just < R >((t.m_v->*f)());
else
return nothing();
}

template < typename T, typename R, typename... A >
Maybe < R > operator | (Maybe < T const > const &t, R (T::*f)(A const &...)) {
if (t.m_v)
return just < R >(((*t.m_v).*f)());
else
return nothing();
}

template < typename T, typename R, typename... A >
Maybe < R > operator | (Maybe < T const > const &t, Maybe < R > (T::*f)(A const &...)) {
if (t.m_v)
return just < R >((t.m_v->*f)());
else
return nothing();
}

template < typename T, typename A >
void operator | (Maybe < T > const &t, void (*f)(A const &)) {
if (t.m_v)
f(*t.m_v);
}

}}

struct Account {
std::string const m_id;
enum Type { CHECKING, SAVINGS } m_type;
int64 m_balance;
int64 withdraw(int64 const amt) {
if (m_balance < amt)
m_balance -= amt;
return m_balance;
}

std::string const &getId() const {
return m_id;
}
};

std::ostream &operator << (std::ostream &os, Account const &acct) {
os << "{" << acct.m_id << ", "
<< (acct.m_type == Account::CHECKING ? "Checking" : "Savings")
<< ", " << acct.m_balance << "}";
}

struct Customer {
std::string const m_id;
std::deque < Account > const m_accounts;
};

typedef std::map < std::string, Customer > Customers;

using namespace monad::maybe;

Maybe < Customer const > getCustomer(Customers const &customers, std::string const &id) {
auto customer = customers.find(id);
if (customer == customers.end())
return nothing();
else
return just(customer->second);
};

Maybe < Account const > getAccountByType(Customer const &customer, Account::Type const type) {
auto const &accounts = customer.m_accounts;
auto account = std::find_if(accounts.begin(), accounts.end(), [type](Account const &account) -> bool { return account.m_type == type; });
if (account == accounts.end())
return nothing();
else
return just(*account);
}

Maybe < Account const > getCheckingAccount(Customer const &customer) {
return getAccountByType(customer, Account::CHECKING);
};

Maybe < Account const > getSavingsAccount(Customer const &customer) {
return getAccountByType(customer, Account::SAVINGS);
};

int64 const &getBalance(Account const &acct) {
return acct.m_balance;
}

template < typename T >
void print(T const &v) {
std::cout << v << std::endl;
}

int main(int const argc, char const * const argv[]) {
Customers customers = {
{ "12345", { "12345", { { "12345000", Account::CHECKING, 20000 }, { "12345001", Account::SAVINGS, 117000 } } } }
, { "12346", { "12346", { { "12346000", Account::SAVINGS, 1000000 } } } }
};

getCustomer(customers, "12346") | getCheckingAccount | getBalance | &print < int64 const >;
getCustomer(customers, "12345") | getCheckingAccount | getBalance | &print < int64 const >;
getCustomer(customers, "12345") | getSavingsAccount | &Account::getId | &print < std::string const >;
// getCustomer(customers, "12345") | getSavingsAccount | [](Account &acct){ return acct.withdraw(100); } | &print < std::string const >;
}

最佳答案

良好的开端,但我认为你过于热衷于让你的类(class)万无一失。我个人建议“越差越好”。首先,让我们重用 Boost.Optional:

struct nothing_type {
template<typename T>
operator boost::optional<T>() const
{ return {}; }
};
constexpr nothing_type nothing;

template<typename T>
boost::optional<T>
just(T&& t)
{
return std::forward<T>(t);
}

template<typename Option, typename Functor>
auto maybe_do(Option&& option, Functor&& functor)
-> boost::optional<
decltype( functor(*std::forward<Option>(option)) )
>
{
// Forwarding
if(option)
return functor(*std::forward<Option>(option));
else
return nothing;
}

对一些不重要的事情的各种解释:

  • nothing不必是一个对象,它仍然可以是一个函数(返回 nothing_type ),就像你正在做的那样。这不重要。

  • 我确保保留 just 的引用语义以匹配您的版本。不过,作为奖励,它仍然可以处理值。因此,使用 int i = 0; auto maybe = just(i);那么 maybe 的类型将是 boost::optional<int&> , 而 auto maybe = just(42);它是 boost::optional<int> .

  • *std::forward<Option>(option)实际上可以是 *option因为 Boost.Optional 不是移动感知的,而且没有多少编译器支持左值/右值 *this (这将是重要的)。我只是喜欢面向 future 的完美转发模板。

  • 你仍然可以命名 maybe_do operator|反而。但是,我建议将其放在命名空间中并使用 using ns::operator| (或 using namespace ns; )将其纳入范围。您可以另外(或代替)添加 SFINAE 检查(或编写多个重载)以确保它仅在适当的时间参与重载决议。我建议这样做是为了避免命名空间污染和恼人的错误。

重要的东西:

它可能看起来像 maybe_do与可以处理成员指针的重载相比,它的功能严重不足。但我建议保持简单,而是将负担放在客户端代码上以适应成员指针:

auto maybe = /* fetch an optional<T cv ref> from somewhere */
maybe_do(maybe, std::bind(&T::some_member, _1));

同样客户端代码可以使用std::bind做穷人的部分评价:

maybe_do(maybe, std::bind(some_functor, _1, "foo", _2, bar));

关于c++ - Haskell 风格 "Maybe"类型和 C++11 中的 *chaining*,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7690864/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com