33

我一直在研究 Ruby,发现它的关键字“直到”和“除非”非常有趣。所以我想什么是在 C/C++ 中添加类似关键字的好方法。这就是我想出的:

#define until(x)    while(!(x))
#define unless(x)   if(!(x))

我正在寻找这方面的一些建议。任何人都可以提出更好的选择吗?

这是我编写的一个程序示例,用于说明我打算做什么:

#include <stdio.h>
#include <stdlib.h>

#define until(x)    while(!(x))
#define unless(x)   if(!(x))

unsigned int factorial(unsigned int n) {
    unsigned int fact=1, i;
    until ( n==0 )
        fact *= n--;
    return fact;    
}

int main(int argc, char*argv[]) {
    unless (argc==2)
        puts("Usage: fact <num>");
    else {
        int n = atoi(argv[1]);
        if (n<0)
            puts("please give +ve number");
        else
            printf("factorial(%u) = %u\n",n,factorial(n));
    }
    return 0;
}

如果您能指出一些可以在 C 或 C++ 中使用的类似技巧的参考资料,那就太好了。

4

10 回答 10

109

任何人都可以提出更好的选择吗?

是的。根本不要这样做。只需直接使用whileandif语句。

当您使用 C 或 C++ 编程时,请使用 C 或 C++ 编程。虽然untilandunless可能在某些语言中经常使用且惯用语,但它们不在 C 或 C++ 中。

于 2011-04-09T19:26:57.207 回答
17

在我看来,你做这件事的方式是正确的,如果你真的要做的话。因为宏的扩展与您期望的非常相似[1],我认为使宏看起来像语法 () 是有效的,而不是通常推荐的用于显示此代码的 SCARY_UPPERCASE_MACROS() '不要遵循通常的语法,你应该小心使用它。

[1] 唯一的缺陷是无法声明变量,这无论如何都不太可能,并且在使用不正确时可能会在正确的位置产生错误,而不是做一些奇怪的事情。

此外,即使是可读性的小幅提高也很重要,因此能够说until (而不是while (!真的确实可以更容易地阅读许多循环。如果结束条件更容易被认为是异常条件(无论是否),那么以这种方式编写循环会更容易阅读。所以即使它只是语法糖,我认为有理由考虑它。

不过我觉得不值得。好处很小,因为大多数程序员都习惯阅读if (!,而且成本是真实的:任何阅读代码的人都必须检查这是宏还是自定义编译器,以及它是否符合他们的想法。它可能会误导你认为你可以做类似的事情i=5 unless xxxx;。如此小的改进,如果广泛传播,将会使语言变得支离破碎,所以通常最好以标准方式做事,并慢慢采用改进。

然而,它可以做得很好:整个 boost 和 tr1,尤其是用模板完成的东西,看起来像是库的扩展,涉及以各种方式扩展 C++,其中许多没有被采用,因为它们似乎不值得它,但其中许多的使用量很小或非常广泛,因为它们做出了真正的改进。

于 2011-04-12T11:10:54.700 回答
13

这让我想起了我在某人的代码中看到的一些东西:

#define R return;

此外,使代码难以理解,增加了维护成本。

于 2011-04-09T19:30:46.970 回答
8

我建议最好不要使用它们。

你不能在 Ruby 风格中使用它们

`printf("hello,world") unless(a>0);`

是非法的。

C 程序员更难理解代码。同时,额外的宏可能是个问题。

于 2011-04-09T19:29:28.990 回答
5

如果您要定义宏,最好让它们看起来非常难看。特别是,它们应该是全大写的,并且有某种前缀。这是因为没有命名空间,也没有与 C++ 的类型系统或重载决议协调。

因此,如果您的宏被调用BIGYAN_UNNECESSARY_MACRO_UNTIL,那么它就不会完全“超越苍白”。

如果您想使用新的循环结构扩展 C++,请考虑研究 C++0x 中的 lambda,您可以在其中允许:

until([&] { return finished; }, [&] 
{
    // do stuff
});

它并不完美,但它比宏好。

于 2011-04-09T19:30:34.127 回答
5

我不认为你的宏不好,特别是如果它们只用于你自己的代码库。 这篇文章 对你来说可能很有趣。话虽如此,当我们在 C++ 中使用宏时,我发现它们有一些缺点。
例如,我们不能写成:

until (T* p = f(x)) ...
unless (T* p = f(x)) ...

另一方面,我们可以写成:

while (T* p = f(x)) ...
if (T* p = f(x)) ...

至于unless,如果我们将其定义为:

#define unless(x) if (x) {} else

然后我们可以写unless (T* p = f(x)) ...。但是,在这种情况下,我们不能在else其后添加子句。

于 2011-04-09T20:36:28.783 回答
2

看看 boost foreach 是如何完成的。

标头定义了 BOOST_FOREACH(丑陋的前缀宏)。你可以

#define foreach BOOST_FOREACH

在您的 .cpp 文件中,以便获得更清晰的代码。但是,您不应该在 .h 文件中执行此操作,而应使用丑陋的 BOOST_FOREACH 代替。

现在,这里有一组“函数式编程”宏,用于“方便”的 IF THEN ELSE 表达式(因为 ?: 很难看):

#define IF(x) (x) ?
#define ELSE :

现在

int x = IF(y==0) 1
        ELSE IF(y<0) 2*y
        ELSE 3*y;

脱糖成:

int x = (y==0) ? 1 : (y<0) ? 2*y : 3*y;
于 2011-04-09T23:51:07.790 回答
2

良好的语法糖示例(恕我直言):

struct Foo {
    void bar() {}
};
typedef std::vector<Foo*> FooVector;
typedef boost::ptr_vector<Foo> FooPtrVector;

FooVector v1;
for (FooVector::iterator it = v1.begin(); it != v1.end(); ++it)
    (*it)->bar(); // ugly

FooPtrVector v2;
for (FooPtrVector::iterator it = v2.begin(); it != v2.end(); ++it)
    it->bar(); // nice
于 2012-08-22T08:30:25.040 回答
1

正如人们所说,添加这些词并不能真正提供有用的语法糖,因为读取一段时间 ( 或 if (! 的成本很小,所有 C 开发人员都习惯了,使用这样的宏你会吓到大部分C 开发人员。此外,让一种语言看起来像另一种语言也不是一个好主意。

但是,语法糖很重要。如前所述,在 C++ 中,boost 通过模板添加了很多语法糖,并且 stl 还提供了 Somme 糖(例如,std::make_pair(a, b)std::pair<decltype(a), decltype(b)>(a, b).

随着语言的改进,功能和语法糖都被添加以提高开发人员的可读性、可写性和效率。例如,在 C++11 规范中,添加了“for(数据结构中的元素)”(见下文),以及允许一周推断类型的“auto”关键字(我说弱,因为您需要键入在很多地方的很多类型实际上是“明显的”和冗余的)。

此外,在 haskell 中,使用没有 do 表示法(语法糖)的 monad 将是一种真正的痛苦,没有人会使用它们1


一个没有语法糖的例子:

//C++ < 11
std::vector<int> v;
v.push_back(3);
v.push_back(7);
v.push_back(9);
v.push_back(12);
for (std::vector<int>::iterator it = v.begin();
     it != v.end();
     it++)
{
    std::cout << *it << std::endl;
}

并使用语法糖:

//C++ >= 11
std::vector<int> v {3, 7, 9, 12};

for (auto elm : v)
{
    std::cout << elm << std::endl;
}

更具可读性,不是吗?


IO monad 的 haskell 示例(来自HaskellWiki):

f :: IO String
f =
  ask "What's your name ?" >>= \name ->
  putStrLn "Write something." >>= \_ ->
  getLine >>= \string ->
  putStrLn ("Hello " ++ name ++ " you wrote " ++ string) >>= \_ ->
  return name

g :: IO String    
g = do
  name <- ask "What's your name ?"
  putStrLn "Write something."
  string <- getLine
  putStrLn ("Hello " ++ name ++ " you wrote " ++ string)
  return name

这是 ideone 的链接:http: //ideone.com/v9BqiZ


1 : 实际上,该语言比 C++ 更灵活,并且允许创建运算符(例如 &^、+.、:+:、...),所以我们可以想象有人会很快再次引入语法糖 :)。

于 2013-07-22T12:49:59.280 回答
-1

好吧,您可以这样做,但请确保它不在源文件中。我建议在没有优化生成的情况下对 JavaScript 采用 CoffeeScript 方法。

总的来说,您应该编写您的语言,但导出、提供和拥有转换后的代码,就好像您用 C 编写的那样具有极高的兼容性。

尝试查看awk并使其转换所有以.cugar保存或类似内容结尾的文件。:)

祝你好运。

于 2018-10-01T15:31:51.967 回答