【问题标题】:Suggestions on syntax to express mathematical formula concisely简洁表达数学公式的语法建议
【发布时间】:2010-05-24 17:23:13
【问题描述】:

我正在 C++ 中开发功能领域特定的嵌入式语言,以尽可能简洁准确地将公式转换为工作代码。

我在 cmets 中发布了一个原型,大约有两百行。

现在我的语言看起来像这样(嗯,实际上看起来像这样):

// implies two nested loops j=0:N, i=0,j
(range(i) < j < N)[T(i,j) = (T(i,j) - T(j,i))/e(i+j)];

// implies summation over above expression
sum(range(i) < j < N))[(T(i,j) - T(j,i))/e(i+j)];

我正在寻找可能的语法改进/扩展,或者只是关于尽可能清晰准确地表达数学公式的不同想法(使用任何语言,而不仅仅是 C++)。

您能否给我一些与我的问题相关的语法示例,这些示例可以用您认为有用的您选择的语言来完成。特别是,如果您对如何翻译上述代码段有一些想法,我会很高兴听到它们。

谢谢。

只是为了澄清并给出一个实际的公式,我的短期目标是表达以下内容

表达式简洁,其中&lt;&gt; 中的值已被计算为 4 维数组。

【问题讨论】:

  • 所以这实际上是 C++ 代码?有趣的想法!
  • @Thomas 是的,我正在 boost phoenix 之上构建
  • 您在实施方面遇到了哪些困难?我猜你知道表达式模板?
  • @gf 谢谢。我现在并没有被任何特别的东西困住。我只是想扩大我对替代样式/语法的经验(因此是社区维基)。是的,我确实知道表达式模板(实际上希望是我的下一个项目)。
  • @Paul 来自计算化学 (/physics),en.wikipedia.org/wiki/…

标签: c++ math programming-languages syntax language-design


【解决方案1】:

如果您要为 ab-initio 世界(我从您的 MP2 方程式中猜测)编写此代码,您希望能够非常简单明了地表达尽可能接近数学定义的事物.

首先,我不会有复杂的range 函数。让它定义一个循环,但如果你想要嵌套循环,请同时指定它们:

所以不是

(range(i) &lt; j &lt; N)[T(i,j) = (T(i,j) - T(j,i))/e(i+j)];

使用

loop(j,0,N)[loop(i,0,j)[T(i,j) = (T(i,j) - T(j,i))/e(i+j)]]

对于 sum 和 product 之类的东西,让语法“继承”它是一个循环这一事实。

所以不是

sum(range(i) &lt; j &lt; N))[(T(i,j) - T(j,i))/e(i+j)];

使用

sum(j,0,n)[loop(i,0,j)[(T(i,j) - T(j,i))/e(i+j)]]

或者如果您需要双倍总和

sum(j,0,n)[sum(i,0,j)[(T(i,j) - T(j,i))/e(i+j)]]

由于您似乎在尝试表示量子力学运算符,因此请尝试使您的语言结构尽可能与运算符匹配 1-1。这样就很容易翻译(并且清楚正在翻译的内容)。

编辑添加

因为您正在研究量子化学,所以它相当容易(至少在语法上如此)。您定义的运算符总是在它们的右侧工作,然后您唯一需要的另一件事是括号来分组运算符停止的位置。

爱因斯坦表示法很有趣,因为您不指定索引或边界,并且由于约定而隐含它们,但这并不能使代码清晰且更难思考。

对于总和,即使隐含界限,它们也总是很容易根据上下文计算出来,因此您应该始终让人们指定它们。

sum(i,0,n)sum(j,0,i)sum(a,-j,j)sum(b,-i,i)....

由于每个运算符都向右工作,它的变量是已知的,所以 j 可以知道 i,a 可以知道 i 和 j,b 可以知道 i、j 和 a。

根据我与量子化学家的经验(我也是其中之一!),他们不喜欢与他们所写内容大相径庭的复杂语法。他们很乐意将二重和三重和和积分分成单数集合,因为它们只是简写形式。

对称也不会那么难。它只是交换和相加或相乘的集合。我会做一些事情,您指定包含相同且可以交换的元素列表的操作:

c2v(sigma_x,a,b)a+b

这表示在 c2v 操作下 a 和 b 可以被认为是相同的粒子。这意味着任何带有 a 和 b 的方程(例如后面的​​ a+b)都应该转换为 c2v 变换的线性组合。 sigma_x 是您想要应用于您的函数的 c2v 中的操作 (a+b)。如果我没记错的话,那是 1/sqrt(2)((a+b)+(b+a))。但是我这里没有我的对称书,所以这可能是错误的。

【讨论】:

  • 是的,量子化学。实际上,我的目标是重现公式是如何用隐含限制的速记符号编写的。算子是我的下一个项目,他们的主要困难是引入某种对称机制
【解决方案2】:

我更喜欢循环之间更牢固的分离。例如,与您的第二个示例相比,我更喜欢这种替代表示法:

sum(range(j) &lt; N)[sum(range(i) &lt; j)[(T(i,j) - T(j,i))/e(i+j)]]

我还发现范围语法很困难。我认为一个范围应该是一个单一的组件。我更喜欢这样的东西:

j = range_iter(0,N)

这将为range x(0,N); j = range.begin(); 或我现在想不出的替代方案开放。

你甚至可以:

j = range_iter(inc(0) =&gt; exc(N)); for j 迭代 [0,N)。

无论如何,有趣的想法。你的结果函数可以组合吗?可以索取域名信息吗?

【讨论】:

  • 实际上,我的例子是sum(range(i) &lt; j, range(j) &lt; N) 的语法糖。我想我可以让作曲工作,但现在不,还没有。域信息、矩阵维度是什么意思?
  • 所有函数都有一个域和范围,记得吗?函数被定义为从一个集合、一个域到另一个集合、范围的映射,这样对于域中的任何元素,在映射到它的范围内都有一个元素(不一定相反)。
  • 你好。抱歉迟了回应。我发布了粗略的原型作为答案,也许它会觉得它很有趣。就领域而言,我认为这可能是可能的,但尚未实施。合成各种作品,但不是我最终想要让它看起来像的方式
【解决方案3】:

如果您正在寻找简单性,您应该进一步了解循环的隐含性。例如,像这样的

T( i < j , j < N ) = ( T(i,j) - T(j,i) )/e(i+j)

如果你重写赋值运算符= 使其对a(i) = b(i) + c(i) 之类的东西表现正常,但对a(i&lt;5) = b(i) + c(i) 表现得像一个求和,则将起作用。除非指定了下限,否则假设总和从 0 开始,例如a(3&lt;i&lt;5),检查显示为总和索引的符号上限/下限,并根据需要进行双倍总和。如果您希望语法强制明确,您可以定义一个单独的求和运算符s=

T( i < j , j < N ) s= ( T(i,j) - T(j,i) )/e(i+j)

我不认为你可以得到比这更干净的东西并且仍然具有一些通用的可用性。至于您的短期目标,使用在索引首次出现的同时指定总和索引的概念,您可以编写。

E_MP2 s= EV( i < n1 , j < n2 , a < n3 , b < n4 ) *
         2 ( EV(a,b,i,j) - EV(a,b,j,i) ) / ( e(i)+e(j)-e(a)-e(b) )

您明确声明这是一个总和(使用s=),使该运算符然后从第一个出现索引的实例中获取总和索引和限制值。具体来说,您还可以使用类似的语法(假设现在 a,b 固定和 i,j 根据您的示例)

E_MP2 s=(i<j,j<N) EV(i,j,a,b) *
                  2 ( EV(a,b,i,j) - EV(a,b,j,i) ) / ( e(i)+e(j)-e(a)-e(b) )

这在符号上很清楚。

然后,您可以通过定义一个集成运算符i= 来做同样的事情,从而进一步了解这个概念。 IE。它查找标记有限制的变量的实例,然后继续针对这些变量对表达式进行积分

F i=(0<x<Pi) x^-1 * exp(-I x^2)

类似于求和,您可以在 x 首次出现时指定限制

F i= x[0,Pi]^-1 * exp(-I x^2)

方括号用于区分符号和求和,因此您不必使用i=s= 并且可以同时使用求和和积分:

F(i) = G(i,j<10) * x[0,inf]^-1 * H(i,j,x)

【讨论】:

  • 最后一个公式很漂亮。
  • 谢谢!我越想这个符号我就越喜欢它。嗯,一直在找项目自学python……
  • 唯一缺少的是推导,你可以使用d=和像这样的花括号:F = Log(x{x0}/A)
【解决方案4】:

我对 Phoenix 不熟悉,只能对其功能做出假设。

我一直很喜欢 Haskell 允许我将范围表达为列表理解的方式。它很好地从实际的数学符号转换为编程语言结构。

[ i + j | i <- [1..10], j <- [1..10] ]

最终会是这样的:

[ i + j | i = range(1,10), j = range(1,10) ]

不幸的是,我真的不知道这样的事情在 Phoenix 上是否可行。

【讨论】:

    【解决方案5】:
    T = T - T'/e;
    

    或者,如果您没有在所有 T 上进行操作

    T(0:i,0:j) = T(0:i,0:j) - T(0:i,0:j)'/e(0:i,0:j)
    

    【讨论】:

    • fortran 90?不幸的是,我很难弄清楚如何在 C++ 中实现 : 运算符。求和类型的公式怎么样,有什么特别的想法吗?只是稍微澄清一下(这是故意的)分母是e(i+j)
    • @aaa:你根本无法实现,Mark 可能忽略了 DSEL 的 embedded 部分。
    • @gf 哦,不,这很好。我正在研究并对所有语法和样式以及扩展思维的可能性感兴趣。顺便说一句,我已经将 ROSE 视为实现范围运算符的可能拐杖。你对此有什么感觉吗?
    • @aaa: 啊,现在我明白了——我的误解:)
    • @Georg:Mark 没有忽视嵌入式部分,但可能对嵌入式设备的强大功能抱有不切实际的期望。 OP 要求提供有关语法的建议,这就是我提供的(嗯,我试过了)。
    【解决方案6】:

    我不喜欢您指定“三角形”二维范围的方式。 我想看到类似的东西:

    (i,j) 在范围内 (0..j,0..N)

    例如,这可能导致:

    X = sum(f(i,j) for (i,j) in range(0..j,0..N));

    AFAIK 这不是一种现有的语言,但是由于您正在创建自己的语法...我怀疑在 i 的范围表达式中使用 j 的能力,但是您在自己的语言中找到了一种方法 :-)

    【讨论】:

    • 嗨。我发布的原型是一些粗略的示例,稍作修改。如果您愿意,可以对更改发表评论吗?
    • 这与使用列表理解在 python 中编写的内容非常相似。 X = sum([f(i,j) for i, j in zip(range(0,j),range(0,N))])(并且定义一个 2D 范围函数来避免 zip 非常容易)。
    • @kriss:是的,我的目标是 Python 语法对它来说还是新的,所以我在不知不觉中添加了 i,j 周围的括号。此外,他需要 0
    【解决方案7】:

    我不确定这些公式会有多复杂,但如果你的 API 看起来更像数学领域而不是标准 C++(使用运算符重载和模板元编程很容易完成),我猜你应考虑开发领域特定语言 (DSL)。当您尝试用一种语言(如您的情况)进行操作时,它被称为内部 DSL,虽然它有一些优点,但也有很多缺点。您应该最了解您的需求,但是我建议您查看用于外部 DSL 的工具,这些工具是专门用于特定领域的小型外部语言。看看 Jetbrains MPS 和 Eclipse Xtext,这是两个开源工具,可以用于快速的外部 DSL 开发。

    【讨论】:

      【解决方案8】:

      我会好好阅读Project Fortress blog,它有一些关于编程语言数学简明符号的鼓舞人心的帖子。

      【讨论】:

        【解决方案9】:

        我会考虑expressing math formulas as they are done in LaTeX。毕竟,LaTeX 在这方面已经有了很好的定义和完善的文档。

        【讨论】:

        • 我不太确定,毕竟 LaTeX 公式是用来显示的,而不是用来构造数学的......
        【解决方案10】:

        我的原型(显然还需要大量工作,还有 cmets)

        // #include "tensor/tensor.hpp"
        // #include "typename.hpp"
        
        #include <boost/spirit/home/phoenix/core/argument.hpp>
        #include <boost/spirit/include/phoenix_core.hpp>
        #include <boost/spirit/include/phoenix_operator.hpp>
        
        #define BOOST_FUSION_INVOKE_FUNCTION_OBJECT_MAX_ARITY PHOENIX_ARG_LIMIT
        #include <boost/fusion/functional/invocation/limits.hpp>
        #include <boost/fusion/functional.hpp>
        
        #include <boost/fusion/include/intrinsic.hpp>
        #include <boost/fusion/include/vector.hpp>
        #include <boost/fusion/include/algorithm.hpp>
        #include <boost/fusion/include/vector_tie.hpp>
        #include <boost/fusion/include/make_vector.hpp>
        
        #include <boost/typeof/typeof.hpp>
        
        namespace range_ {
            namespace detail {
        
                namespace phoenix = boost::phoenix;
                namespace fusion = boost::fusion;
        
                /// undefined or implicit object
                struct undefined {};
        
                template<int N>
                struct index {
                    // index(boost::phoenix::argument<N>) {}
                    typedef phoenix::actor<phoenix::argument<N> > argument;
                    argument arg() const { return argument(); }
                };
        
                template<typename T, typename U = void>
                struct functional {
                    typedef phoenix::actor<phoenix::value<T> > type;
                    static type form(const T& t) { return type(t); }
                };
        
                template<typename T, typename U>
                struct functional<phoenix::actor<T>, U> {
                    typedef phoenix::actor<T> type;
                    static type form(const T& t) { return type(t); }
                };
        
                template<typename T>
                struct functional<undefined,T> {
                    typedef typename functional<T>::type type;
                    static type form(const undefined&) { return type(T()); }
                };
        
                template<int N, class L, class U, class T = undefined>
                struct expression;
        
                template<int N, class L, class U, class C>
                struct expression {
                    typedef functional<L,U> lower_function;
                    typedef functional<U,L> upper_function;
        
                    expression(const L &lower, const U& upper, const C &cdr)
                        : lower_(lower), upper_(upper), cdr_(cdr) {}
        
                    template<class E>
                    expression<N, L, U, E> operator()(const E &c) const {
                        return expression<N, L, U, E>(lower_,  upper_, c);
                    }
        
                    template<class F>
                    void operator[](const F &f) const {
        #define TEXT(z, n, text) text
        #define UNDEFINED_ARGUMENTS BOOST_PP_ENUM(PHOENIX_ARG_LIMIT, TEXT, undefined())
                        evaluate<int>(f, fusion::make_vector(UNDEFINED_ARGUMENTS));
        #undef TEXT
        #undef UNDEFINED_ARGUMENTS
                    }
        
                    L lower_;
                    U upper_;
                    C cdr_;
        
                    const L& left() const { return lower_; }
        
                    const C& cdr() const {return cdr_; }
        
                    template<typename T>
                    typename functional<L,T>::type begin() const {
                        return functional<L,T>::form(lower_);
                    }
        
                    template<typename T>
                    typename functional<U,T>::type end() const {
                        return functional<U,T>::form(upper_);
                    }
        
                    template<typename T, class F, class A>
                    void evaluate(const F &f, const A &arguments) const {
                        T i = this->begin<T>()(arguments);
        #define AS_VECTOR(var, expr) BOOST_AUTO(var, fusion::as_vector(expr))
        #define ADVANCE_C(seq) fusion::advance_c<N>(fusion::begin(seq))
                        AS_VECTOR(b, fusion::erase(arguments, ADVANCE_C(arguments)));
                        AS_VECTOR(a, fusion::insert_range(b, ADVANCE_C(b),
                                                          fusion::vector_tie(i)));
        #undef ADVANCE_C
        #undef AS_VECTOR
                        while (fusion::invoke_function_object(this->end<T>(), a)) {
                            this->apply<T>(cdr_, f, a);
                            ++i;
                        }
                    }
        
                    template<typename T, class E, class F, class V>
                    void apply(const E &e, const F &f, const V &variables) const {
                        e.template evaluate<T>(f, variables);
                    }
        
                    template<typename T, class F, class V>
                    void apply(const undefined&, const F &f, const V &variables) const {
                        fusion::invoke_function_object(f, fusion::as_vector(variables));
                    }
        
                };
        
                template<int N, class  L, class U>
                expression<N, L, U>
                make_expression(const L &lower, const U& upper) {
                    return expression<N, L, U>(lower, upper, undefined());
                }
        
                template<int N, class  L, class U>
                expression<N, L, U>
                make_expression(const expression<N, L, undefined> &expr, const U& right) {
                    return expression<N, L, U>(expr.left(), right, undefined());
                }
        
                template<int N1, class L1, class U1, class T1,
                         int N2, class L2, class U2>
                expression<N2, L2, U2, expression<N1, L1, U1, T1> >
                operator,(const expression<N1, L1, U1, T1> &e1,
                          const expression<N2, L2, U2> &e2)  {
                    return e2(e1);
                }
        
        #define ARGUMENT(N) phoenix::actor<phoenix::argument<N> >
        #define ACTOR_COMPOSITE(O,L,R)                                          \
                phoenix::actor<typename phoenix::as_composite<O, L, R>::type>
        
        
        #define LOWER_BOUND_OPERATOR(op, eval, param)                           \
                template <typename T, int N>                                    \
                expression<N, param, undefined>                                 \
                operator op (const param& left, const index<N> &i) {            \
                    return make_expression<N>(left, undefined());               \
                }
        
        
        #define UPPER_BOUND_OPERATOR_INDEX(op, eval, param)             \
                template <typename T, int N>                            \
                expression<N, undefined,                                \
                           ACTOR_COMPOSITE(eval, ARGUMENT(N), param)>   \
                operator op (const index<N> &i, const param& e) {       \
                    return make_expression<N>(undefined(),              \
                                              (ARGUMENT(N)() op e));    \
                }
        
        #define UPPER_BOUND_OPERATOR_EXPRESSION(op, eval)                       \
                template <typename T, int N, class E>                           \
                expression<N, E, ACTOR_COMPOSITE(eval, ARGUMENT(N), T)>         \
                operator op (const expression<N, E, undefined> &left,           \
                             const T& right) {                                  \
                    return make_expression(left, (ARGUMENT(N)() op right));     \
                }
        
        #define UPPER_BOUND_OPERATOR(op, eval)                                  \
                UPPER_BOUND_OPERATOR_INDEX(op, eval, T)                         \
                UPPER_BOUND_OPERATOR_INDEX(op, eval, phoenix::actor<T>)         \
                UPPER_BOUND_OPERATOR_EXPRESSION(op, eval)
        
                LOWER_BOUND_OPERATOR( < , phoenix::less_eval, T)
                LOWER_BOUND_OPERATOR( < , phoenix::less_eval, phoenix::actor<T>)
                LOWER_BOUND_OPERATOR( <= , phoenix::less_equal_eval, T)
                LOWER_BOUND_OPERATOR( <= , phoenix::less_equal_eval, phoenix::actor<T>)
        
                UPPER_BOUND_OPERATOR( < , phoenix::less_eval)
                UPPER_BOUND_OPERATOR( <= , phoenix::less_equal_eval)
        
            }
        
        }
        
        
        namespace index {
            using namespace boost::phoenix;
            boost::phoenix::actor<boost::phoenix::argument<0> > const i;
            boost::phoenix::actor<boost::phoenix::argument<1> > const j;
            boost::phoenix::actor<boost::phoenix::argument<2> > const k;
            boost::phoenix::actor<boost::phoenix::argument<3> > const l;
        
            template<int N>
            range_::detail::index<N> range(const boost::phoenix::actor<
                                           boost::phoenix::argument<N> >&) {
                return range_::detail::index<N>();
            }
            template<int N, class F>
            range_::detail::index<N> range(const boost::phoenix::actor<
                                           boost::phoenix::argument<N> >&,
                                           const F &f) {
                // return range_::detail::index<N>();
                throw std::exception("not implemented");
            }
        
        
        
        }
        
        int main(){
        
            using namespace index;
        
            // formula domain language rough prototype
            // stuff in brackets can be any valid phoenix lambda expression
        
            // physicist notation, lower bound may be implicit
            (range(i) <= j, 3 <= range(j) < 4)[std::cout << i << " " << j << std::endl];
        
            // implicit physicist notation, not done yet
            //(range(i) < range(j) < 4)[...]
        
            // programmer notation, same as above , but order is reversed
            (3 <= range(j) < 4)(range(i) <= j)[std::cout << i << " " << j << std::endl];
        
            // ignore, some early prototype for lambda tensor
            // size_t N = 4;
            // tensor::tensor<4> T(N,N,N,N);
        
             // tensor::function<tensor::tensor<4> > T_(T);
            // (range(j) < 4)(range(i) <= j)[std::cout << T_(i,j,0,0)];
        
            // (range(i) < j, range(j) < N)[T_(i,j,0,0) = T_(j,i,0,0)];
            // sum(j < val(N))[T_(i,j,0,0)];
        
        }
        

        【讨论】:

          【解决方案11】:

          您可能想从 APLish 语言中寻找灵感。通过将矩阵/数组作为一个整体使用,您可以大大降低简洁性。我将在下面使用Q,除非我看错了您的帖子,否则您的第二个陈述可以写成:

          sum raze (T-flip T) div e
          

          要了解 T 减去翻转 T,您需要看一个示例。假设我们将 T 设置为以下矩阵:

          0 1 2
          3 4 5
          6 7 8
          

          flip T 交换顶部的两个维度,结果是:

          0 3 6
          1 4 7
          2 5 8
          

          所以T-flip T 基本上是T(i,j)-T(j,i),但打字少了很多,因此更不容易出错。

          raze 将数组缩减为一维,因此将其夷为平地:

          0 3 6
          1 4 7
          2 5 8
          

          把它变成这样:

          0 3 6 1 4 7 2 5 8
          

          最后,sum 是求和;它基本上将其列表中的所有成员加起来,所以

          sum 0 3 6 1 4 7 2 5 8
          

          意思是:

          0+3+6+1+4+7+2+5+8
          

          在 C++ 中实现这种东西并不难;你可以:

          sum(raze((T-flip(T))/e))
          

          具有正确级别的运算符重载。

          顺便说一下,K 更简短:

          +/,/(T-+:T)%e
          

          【讨论】:

            【解决方案12】:

            封装!

            我相信您会找到一些平衡简洁和清晰的语法。不管它有多好,通过提供封装将大大改善它。所以不是

            qty = sum(range(i) < j < N))[(T(i,j) - T(j,i))/e(i+j)];
            

            你可以有

            Texpr = (T(i,j) - T(j,i))/e(i+j);
            RangeExpr = (range(i) < j < N);
            qty = sum(RangeExpr)[ Texpr ];
            

            这可能会让你的语法更加冗长。

            PS:这不是 Mathematica 吗? Mathematica C/C++ Language Interface

            【讨论】:

              【解决方案13】:

              您应该查看用于Haskell's list comprehension 的语法。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2012-11-15
                • 2012-07-20
                • 1970-01-01
                • 1970-01-01
                • 2013-08-29
                相关资源
                最近更新 更多