草庐IT

c++ - 用实际元素初始化 boost::multi_array 的干净方法

coder 2024-02-23 原文

我正在寻找干净的语法糖来从显式值初始化 boost::multi_array。我能想到的最好的办法是

double g[5][5] = {
{-0.0009    ,  0.003799 ,    0.00666   ,   0.00374   ,   0.00186 },
{-0.0008    ,  0.0176   ,    0.0619    ,   0.0159    ,   0.00324 },
{0.00099    ,  0.0475   ,    0.666     ,   0.0376    ,   0.00758 },
{0.00242    ,  0.02189  ,    0.0624    ,   0.0192    ,   0.0008 },
{0.00182    ,  0.00404  ,    0.00479   ,   0.00924   ,   0.00189 }};

boost::multi_array_ref<double,2> mg((double*)g,boost::extents[5][5]);

我不喜欢这样,因为它需要 2 个变量而不是 1 个,三重冗余 [5][5] 维度(大小可以通过嵌套的大括号列表推断),以及从 double[][]double* 的转换。

我确实喜欢这样一个事实,即没有从 g 执行复制到 mg,并且 g 的初始化方式是赏心悦目(即嵌套的、结构化的初始化列表,具有最少的多余字符)。

最佳答案

有几个选项可用。它们都需要一些模板魔法;它们在句法表达能力和效率上有所不同。如果boost::multi_array,生活会更轻松和 friend 们实际上提供了一些更有用的构造函数,但遗憾的是,在撰写本文时情况并非如此。

1) 使用平面 initializer_list

这个基本选项删除了一些冗余,并提供了相当不错的语法糖。它使用一个带有 initializer_list<t> 的辅助函数。 , 将其转储到 std::vector 中,并使用它首先创建一个 const_multi_array_ref然后将其深复制到 multi-array 中.

#include <boost/multi_array.hpp>
#include <cassert>
#include <initializer_list>
#include <vector>

// Helper class to determine the full number of elements in the
// multi-dimensional array
template <std::size_t... vs> struct ArraySize;
template <std::size_t v, std::size_t... vs> struct ArraySize<v, vs...>
{ static constexpr std::size_t size = v * ArraySize<vs...>::size; };
template <> struct ArraySize<>
{ static constexpr std::size_t size = 1; };

// Creates your multi_array
template <typename T, int... dims>
boost::multi_array<T, sizeof...(dims)>
makeMultiArray(std::initializer_list<T> l)
{
  constexpr std::size_t asize = ArraySize<dims...>::size;
  assert(l.size() == asize); // could be a static assert in C++14

  // Dump data into a vector (because it has the right kind of ctor)
  const std::vector<T> a(l);
  // This can be used in a multi_array_ref ctor.
  boost::const_multi_array_ref<T, sizeof...(dims)> mar(
    &a[0],
    std::array<int, sizeof...(dims)>{dims...});
  // Finally, deep-copy it into the structure we can return.
  return boost::multi_array<T, sizeof...(dims)>(mar);
}

// Usage example
auto mg = makeMultiArray<double, 5, 5>({
  -0.0009, 0.003799, 0.00666, 0.00374, 0.00186,
  -0.0008, 0.0176,   0.0619,  0.0159,  0.00324,
  0.00099, 0.0475,   0.666,   0.0376,  0.00758,
  0.00242, 0.02189,  0.0624,  0.0192,  0.0008,
  0.00182, 0.00404,  0.00479, 0.00924, 0.00189});

在此版本中,initializer_list 的适当大小只在运行时检查,但我认为在 C++14 std::initializer_list::size()将是 constexpr ,这应该允许您使用 static_assert .

  • 优点:声明和维度中的冗余已消失。
  • 缺点:至少创建一个拷贝;使用不易读的平面列表。

2) 从 C 数组初始化

这个更接近您的原始数组,但您需要单独定义数组——我认为您不能直接将其作为具有冗余转换的函数参数提供。优点是因为您首先构建了一个标准的 C 数组,所以您可以在 multi_array_ref 中重复使用它。而且您不需要拷贝。与第一个选项相比,您需要一些额外的构造:CArray用于从模板参数构造 C 数组类型。

// CArray<double,1,2,3>::type is double[1][2][3]
template <typename T, std::size_t... vs> struct CArray;
template <typename T, std::size_t v, std::size_t... vs> struct CArray<T, v, vs...>
{ typedef typename CArray<T, vs...>::type type[v]; };
template <typename T> struct CArray<T> { typedef T type; };

// Creates a multi_array_ref
template <typename T, int... dims>
boost::multi_array_ref<T, sizeof...(dims)>
makeMultiArray(typename CArray<T, dims...>::type l)
{
  constexpr std::size_t asize = ArraySize<dims...>::size;
  return boost::multi_array_ref<T, sizeof...(dims)>(
           reinterpret_cast<double*>(l),
           std::array<int, sizeof...(dims)>{dims...});
}

// Usage example
double g[5][5] =
  { { -0.0009, 0.003799, 0.00666, 0.00374, 0.00186 },
    { -0.0008, 0.0176,   0.0619,  0.0159,  0.00324 },
    { 0.00099, 0.0475,   0.666,   0.0376,  0.00758 },
    { 0.00242, 0.02189,  0.0624,  0.0192,  0.0008  },
    { 0.00182, 0.00404,  0.00479, 0.00924, 0.00189 } };
auto mg = makeMultiArray<double, 5, 5>(g);
  • 优点:保持初始化程序的层次结构,以 boost 可读性;避免复制。
  • 缺点:仍然有一些您想要摆脱的冗余。

3) 使用嵌套 initializer_list s

这个似乎更合适,但效率较低。除了上面的代码,我们还需要一个方法来构造嵌套的initializer_list s,并将它们复制到一个数组中。

// Nested initializer lists
template <typename T, std::size_t level> struct NestedInitializerList
{
  typedef std::initializer_list<typename NestedInitializerList<T, level-1>::type> type;
};
template <typename T> struct NestedInitializerList<T, 1>
{
  typedef std::initializer_list<T> type;
};

// Helpers which fill the array from a nested initializer_list
template <typename T>
void fillArray(const T& l, typename CArray<T>::type& a)
{
  a = l;
}
template <typename T, int dim, int... dims>
void fillArray(typename NestedInitializerList<T, sizeof...(dims)+1>::type l,
               typename CArray<T, dim, dims...>::type& a)
{
  assert(l.size() == dim); // could be a static assert in C++14
  int i=0;
  for (auto it = l.begin(); it != l.end(); ++it, ++i)
  {
    fillArray<T, dims...>(*it, a[i]);
  }
}

// Creates your multi_array
template <typename T, int... dims>
boost::multi_array<T, sizeof...(dims)>
makeMultiArray(typename NestedInitializerList<T, sizeof...(dims)>::type l)
{
  typename CArray<T, dims...>::type a; // Multidimensional C array
  fillArray<T, dims...>(l, a);         // Fill from l
  // Turn into multi_array_ref.
  boost::const_multi_array_ref<T, sizeof...(dims)> mar(
    reinterpret_cast<const double*>(a),
    std::array<int, sizeof...(dims)>{dims...});
  // Finally, deep-copy it into the structure we can return.
  return boost::multi_array<T, sizeof...(dims)>(mar);
}

// Usage example
auto mg = makeMultiArray<double, 5, 5>(
  { { -0.0009, 0.003799, 0.00666, 0.00374, 0.00186 },
    { -0.0008, 0.0176,   0.0619,  0.0159,  0.00324 },
    { 0.00099, 0.0475,   0.666,   0.0376,  0.00758 },
    { 0.00242, 0.02189,  0.0624,  0.0192,  0.0008  },
    { 0.00182, 0.00404,  0.00479, 0.00924, 0.00189 } });

最后一个来自 this article 的一点灵感。 .

  • Pro:用法完全如您所愿;无冗余,清晰的层次初始化结构
  • 缺点:fillArray()例程是递归的,因此效率较低(我预计编译器也无法对其进行优化)。

关于c++ - 用实际元素初始化 boost::multi_array 的干净方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35209121/

有关c++ - 用实际元素初始化 boost::multi_array 的干净方法的更多相关文章

  1. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

  2. ruby - 在 Ruby 中实现 `call_user_func_array` - 2

    我怎样才能完成http://php.net/manual/en/function.call-user-func-array.php在ruby中?所以我可以这样做:classAppdeffoo(a,b)putsa+benddefbarargs=[1,2]App.send(:foo,args)#doesn'tworkApp.send(:foo,args[0],args[1])#doeswork,butdoesnotscaleendend 最佳答案 尝试分解数组App.send(:foo,*args)

  3. ruby-on-rails - 未初始化的常量 Psych::Syck (NameError) - 2

    在我的gem中,我需要yaml并且在我的本地计算机上运行良好。但是在将我的gem推送到ruby​​gems.org之后,当我尝试使用我的gem时,我收到一条错误消息=>"uninitializedconstantPsych::Syck(NameError)"谁能帮我解决这个问题?附言RubyVersion=>ruby1.9.2,GemVersion=>1.6.2,Bundlerversion=>1.0.15 最佳答案 经过几个小时的研究,我发现=>“YAML使用未维护的Syck库,而Psych使用现代的LibYAML”因此,为了解决

  4. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  5. Ruby Koans about_array_assignment - 非平行与平行分配歧视 - 2

    通过ruby​​koans.com,我在about_array_assignment.rb中遇到了这两段代码你怎么知道第一个是非并行赋值,第二个是一个变量的并行赋值?在我看来,除了命名差异之外,代码几乎完全相同。4deftest_non_parallel_assignment5names=["John","Smith"]6assert_equal["John","Smith"],names7end45deftest_parallel_assignment_with_one_variable46first_name,=["John","Smith"]47assert_equal'John

  6. ruby-on-rails - 未在 Ruby 中初始化的对象 - 2

    我在Rails工作并有以下类(class):classPlayer当我运行时bundleexecrailsconsole然后尝试:a=Player.new("me",5.0,"UCLA")我回来了:=>#我不知道为什么Player对象不会在这里初始化。关于可能导致此问题的操作/解释的任何建议?谢谢,马里奥格 最佳答案 havenoideawhythePlayerobjectwouldn'tbeinitializedhere它没有初始化很简单,因为你还没有初始化它!您已经覆盖了ActiveRecord::Base初始化方法,但您没有调

  7. ruby-on-rails - ActionController::RoutingError: 未初始化常量 Api::V1::ApiController - 2

    我有用于控制用户任务的Rails5API项目,我有以下错误,但并非总是针对相同的Controller和路由。ActionController::RoutingError:uninitializedconstantApi::V1::ApiController我向您描述了一些我的项目,以更详细地解释错误。应用结构路线scopemodule:'api'donamespace:v1do#=>Loginroutesscopemodule:'login'domatch'login',to:'sessions#login',as:'login',via::postend#=>Teamroutessc

  8. ruby - 这两个 Ruby 类初始化定义有什么区别? - 2

    我正在阅读一本关于Ruby的书,作者在编写类初始化定义时使用的形式与他在本书前几节中使用的形式略有不同。它看起来像这样:classTicketattr_accessor:venue,:datedefinitialize(venue,date)self.venue=venueself.date=dateendend在本书的前几节中,它的定义如下:classTicketattr_accessor:venue,:datedefinitialize(venue,date)@venue=venue@date=dateendend在第一个示例中使用setter方法与在第二个示例中使用实例变量之间是

  9. arrays - 这是 Ruby 中 Array.fill 方法的错误吗? - 2

    这个问题在这里已经有了答案:Arraysmisbehaving(1个回答)关闭6年前。是否应该这样,即我误解了,还是错误?a=Array.new(3,Array.new(3))a[1].fill('g')=>[["g","g","g"],["g","g","g"],["g","g","g"]]它不应该导致:=>[[nil,nil,nil],["g","g","g"],[nil,nil,nil]]

  10. ruby - 在哈希的键数组中追加元素 - 2

    查看我的Ruby代码:h=Hash.new([])h[0]=:word1h[1]=h[1]输出是:Hash={0=>:word1,1=>[:word2,:word3],2=>[:word2,:word3]}我希望有Hash={0=>:word1,1=>[:word2],2=>[:word3]}为什么要附加第二个哈希元素(数组)?如何将新数组元素附加到第三个哈希元素? 最佳答案 如果您提供单个值作为Hash.new的参数(例如Hash.new([]),完全相同的对象将用作每个缺失键的默认值。这就是您所拥有的,那是你不想要的。您可以改用

随机推荐