使用C++来编写Ruby程序扩展的教程

Ruby 最酷的功能之一就是使用 C/C++ 定义的应用程序编程接口 (API) 扩展它。Ruby 提供了 C 头文件 ruby.h,它随附提供了许多功能,可使用这些功能创建 Ruby 类、模块和更多内容。除了头文件,Ruby 还提供了其他几个高层抽象来扩展基于本地 ruby.h 构建的 Ruby,本文要介绍的是 Ruby Interface for C++ Extensions 或 Rice。
创建 Ruby 扩展

在进行任何 Ruby 的 C API 或 Rice 扩展前,我想明确地介绍一下创建扩展的标准过程:

  • 您具有一个或多个 C/C++ 源代码,可使用它们构建共享库。
  • 如果您使用 Rice 创建扩展,则需要将代码链接到 libruby.a 和 librice.a。
  • 将共享库复制到同一文件夹,并将该文件夹作为 RUBYLIB 环境变量的一部分。
  • 在 Interactive Ruby (irb) prompt/ruby 脚本中使用常见的基于 require 的加载。如果共享库名为 rubytest.so,只需键入 require 'rubytest' 即可加载共享库。

假设头文件 ruby.h 位于 /usr/lib/ruby/1.8/include 中,Rice 头文件位于 /usr/local/include/rice/include 中,并且扩展代码位于文件 rubytest.cpp 中。 清单 1 显示了如何编译和加载代码。
清单 1. 编译和加载 Ruby 扩展

bash# g++ -c rubytest.cpp –g –Wall -I/usr/lib/ruby/1.8/include \
  -I/usr/local/include/rice/include
bash# g++ -shared –o rubytest.so rubytest.o -L/usr/lib/ruby/1.8/lib \
  -L/usr/local/lib/rice/lib -lruby –lrice –ldl -lpthread
bash# cp rubytest.so /opt/test
bash# export RUBYLIB=$RUBYLIB:/opt/test
bash# irb
irb> require 'rubytest'
=> true

Hello World 程序

现在,您已经准备好使用 Rice 创建自己的首个 Hello World 程序。您使用名为 Test 的 Rice API 和名为 hello 的方法创建了一个类,用它来显示字符串 "Hello, World!"。当 Ruby 解释器加载扩展时,会调用函数 Init_<shared library name>。对于 清单 1 的 rubytest 扩展,此调用意味着 rubytest.cpp 已定义了函数 Init_rubytest。Rice 支持您使用 API define_class 创建自己的类。清单 2 显示了相关代码。
清单 2. 使用 Rice API 创建类

#include "rice/Class.hpp"
extern "C"
void Init_rubytest( ) {
 Class tmp_ = define_class("Test");
}

当您在 irb 中编译和加载清单 2 的代码时,应得到 清单 3 所示的输出。
清单 3. 测试使用 Rice 创建的类

irb> require ‘rubytest'
=> true
irb> a = Test.new
=> #<Test:0x1084a3928>
irb> a.methods
=> ["inspect", "tap", "clone", "public_methods", "__send__",
   "instance_variable_defined?", "equal?", "freeze", …]

注意,有几个预定义的类方法可供使用,比如 inspect。出现这种情况是因为,定义的 Test 类隐式地衍生自 Object 类(每个 Ruby 类都衍生自 Object;实际上,Ruby 中的所有内容(包括数字)都是基类为 Object 的对象)。

现在,为 Test 类添加一个方法。清单 4 显示了相关代码。
清单 4. 为 Test 类添加方法

void hello() {
  std::cout << "Hello World!";
}
extern "C"
 void Init_rubytest() {
   Class test_ = define_class("Test")
     .define_method("hello", &hello);
}

清单 4 使用 define_method API 为 Test 类添加方法。注意,define_class 是返回一个类型为 Class 的对象的函数;define_method 是 Module_Impl 类的成员函数,该类是 Class 的基类。下面是 Ruby 测试,验证所有内容是否都运行良好:

irb> require ‘rubytest'
=> true
irb> Test.new.hello
Hello, World!
=> nil

将参数从 Ruby 传递到 C/C++ 代码

现在,Hello World 程序已正常运行,尝试将参数从 Ruby 传递到 hello 函数,并让函数显示与标准输出 (sdtout) 相同的输出。最简单的方法是为 hello 函数添加一个字符串参数:

void hello(std::string args) {
  std::cout << args << std::endl;
}
extern "C"
 void Init_rubytest() {
   Class test_ = define_class("Test")
     .define_method("hello", &hello);
}

在 Ruby 环境中,以下是调用 hello 函数的方式:

irb> a = Test.new
<Test:0x0145e42112>
irb> a.hello "Hello World in Ruby"
Hello World in Ruby
=> nil

使用 Rice 最出色的一点是,无需进行任何特殊操作将 Ruby 字符串转换为 std::string。

现在,尝试在 hello 函数中使用字符串数组,然后检查如何将信息从 Ruby 传递到 C++ 代码。最简单的方式是使用 Rice 提供的 Array 数据类型。在头文件 rice/Array.hpp 中定义 Rice::Array,使用 Rice::Array 的方式类似于使用 Standard Template Library (STL) 容器。还要将常见的 STL 样式迭代器等内容定义为 Array 接口的一部分。清单 5 显示了 count 例程,该例程使用 Rice Array 作为参数。
清单 5. 显示 Ruby 数组

#include "rice/Array.hpp"

void Array_Print (Array a)  {
   Array::iterator aI = a.begin();
   Array::iterator aE = a.end();
   while (aI != aE) {
    std::cout << "Array has " << *aI << std::endl;
    ++aI;
   }
 }

现在,下面是此解决方案的魅力所在:假设您拥有 std::vector<std::string> 作为 Array_Print 参数。下面是 Ruby 抛出的错误:

>> t = Test.new
=> #<Test:0x100494688>
>> t.Array_Print ["g", "ggh1", "hh1"]
ArgumentError: Unable to convert Array to std::vector<std::string,
  std::allocator<std::string> >
 from (irb):3:in `hello'
 from (irb):3

但是,使用此处显示的 Array_Print 例程,Rice 负责执行从 Ruby 数组到 C++ Array 类型的转换。下面是样例输出:

>> t = Test.new
=> #<Test:0x100494688>
>> t.Array_Print ["hello", "world", "ruby"]
Array has hello
Array has world
Array has ruby
=> nil

现在,尝试相反的过程,将 C++ 的数组传递到 Ruby 环境。请注意,在 Ruby 中,数组元素不一定是同一类型的。清单 6 显示了相关代码。
清单 6. 将数组从 C++ 传递到 Ruby

#include "rice/String.hpp"
#include "rice/Array.hpp"
using namespace rice; 

Array return_array (Array a) {
   Array tmp_;
   tmp_.push(1);
   tmp_.push(2.3);
   tmp_.push(String("hello"));
   return tmp_;
 }

清单 6 明确显示了您可以在 C++ 中创建具有不同类型的 Ruby 数组。下面是 Ruby 中的测试代码:

>> x = t.return_array
=> [1, 2.3, "hello"]
>> x[0].class
=> Fixnum
>> x[1].class
=> Float
>> x[2].class
=> String

如果我没有更改 C++ 参数列表的灵活性,会怎么样?

更常见的情况是具有这样的灵活性,您将发现 Ruby 接口旨在将数据转换为 C++ 函数,该函数的签名无法更改。例如,考虑需要将字符串数组从 Ruby 传递到 C++ 的情形。C++ 函数签名如下所示:

void print_array(std::vector<std::string> args)

实际上,您在这里寻找的是某种 from_ruby 函数,Ruby 数组使用该函数并将它转换为 std::vector<std::string>。这正是 Rice 提供的内容,具有下列签名的 from_ruby 函数:

template <typename T>
T from_ruby(Object );

对于需要转换为 C++ 类型的每种 Ruby 数据类型,需要针对模板详细说明 from_ruby 例程。例如,如果将 Ruby 数组传递到上述处理函数,清单 7 显示了应如何定义 from_ruby 函数。
清单 7. 将 ruby 数组转换为 std::vector<std::string>

template<>
std::vector<std::string> from_ruby< std::vector<std::string> > (Object o)  {
  Array a(o);
  std::vector<std::string> v;
  for(Array::iterator aI = a.begin(); aI != a.end(); ++aI)
    v.push_back(((String)*aI).str());
  return v;
  }

请注意,不需要显式地调用 from_ruby 函数。当从 Ruby 环境传递作为函数参数的 string 数组时,from_ruby 将它转换为 std::vector<std::string>。清单 7 中的代码并不完美,但是您已经看到,Ruby 中的数组具有不同类型。相反,您调用了 ((String)*aI).str(),以便从 Rice::String 获得 std::string。(str 是 Rice::String 的一种方法:查看 String.hpp 以了解有关的更多信息。)如果您处理的是最常见的情形,清单 8 显示了相关的代码。
清单 8. 将 ruby 数组转换为 std::vector<std::string>(通用情况)

template<>
std::vector<std::string> from_ruby< std::vector<std::string> > (Object o)  {
  Array a(o);
  std::vector<std::string> v;
  for(Array::iterator aI = a.begin(); aI != a.end(); ++aI)
    v.push_back(from_ruby<std::string> (*aI));
  return v;
  }

由于 Ruby 数组的每个元素仍然是类型为 String 的 Ruby 对象,因此可以假设 Rice 已定义了 from_ruby 方法,将此类型转换为 std::string,不需要进行其他操作。如果情况并非如此,则需要为此转换提供 from_ruby 方法。下面是 Rice 资源中 to_from_ruby.ipp 的 from_ruby 方法:

template<>
inline std::string from_ruby<std::string>(Rice::Object x) {
 return Rice::String(x).str();
}

在 Ruby 环境中测试此代码。首先传递所有字符串的数组,如 清单 9 所示。
清单 9. 验证 from_ruby 功能

>> t = Test.new
=> #<Test:0x10e71c5c8>
>> t.print_array ["aa", "bb"]
aa bb
=> nil
>> t.print_array ["aa", "bb", 111]
TypeError: wrong argument type Fixnum (expected String)
 from (irb):4:in `print_array'
 from (irb):4

和预期一样,首次调用 print_array 运行正常。由于没有 from_ruby 方法来将 Fixnum 转换为 std::string,因此第二次调用时,会导致 Ruby 解释器抛出 TypeError。有几种修复此错误的方法:例如,在 Ruby 调用期间,仅将字符串作为数组的一部分(比如 t.print_array["aa", "bb", 111.to_s])来传递,或者是在 C++ 代码中,调用 Object.to_s。to_s 方法是 Rice::Object 接口的一部分,它会返回 Rice::String,它还有一个返回 std::string 的预定义 str 方法。清单 10 使用了 C++ 方法。
清单 10. 使用 Object.to_s 填充字符串向量

template<>
std::vector<std::string> from_ruby< std::vector<std::string> > (Object o)  {
  Array a(o);
  std::vector<std::string> v;
  for(Array::iterator aI = a.begin(); aI != a.end(); ++aI)
    v.push_back(aI->to_s().str());
  return v;
  }

通常,清单 10 中的代码更为重要,因为您需要处理用户定义的类的自定义字符串表示。

使用 C++ 创建一个具有变量的完整类

您已经了解了在 C++ 代码内如何创建 Ruby 类和相关函数。对于更通用的类,需要一种定义实例变量的方法,并提供一个 initialize 方法。要设置并获得 Ruby 对象实例变量的值,可以使用 Rice::Object::iv_set 和 Rice::Object::iv_get 方法。清单 11 显示了相关的代码。
清单 11. 在 C++ 中定义 initialize 方法

void init(Object self) {
   self.iv_set("@intvar", 121);
   self.iv_set("@stringvar", String("testing"));
 }
Class cTest = define_class("Test").
             define_method("initialize", &init);

使用 define_method API 将 C++ 函数声明为 Ruby 类方法时,可选择将 C++ 函数的第一个参数声明为 Object,并且 Ruby 会使用调用实例的引用来填充此 Object。然后,在 Object 上调用 iv_set 来设置实例变量。下面是接口在 Ruby 环境中的外观:

>> require 'rubytest'
=> true
>> t = Test.new
=> #<Test:0x1010fe400 @stringvar="testing", @intvar=121>

同样地,要返回实例变量,返回的函数需要接收在 Ruby 中引用对象的 Object,并对它调用 iv_get。清单 12 显示了相关的代码片段。
清单 12. 从 Ruby 对象检索值

void init(Object self) {
   self.iv_set("@intvar", 121);
   self.iv_set("@stringvar", String("testing"));
 }
int getvalue(Object self) {
  return self.iv_get("@intvar");
}
Class cTest = define_class("Test").
             define_method("initialize", &init).
             define_method("getint", &getvalue);

将 C++ 类转换为 Ruby 类型

迄今为止,您已经将免费的函数(非类方法)包装为 Ruby 类方法。您已经将引用传递给 Ruby 对象,方法是使用第一个参数 Object 声明 C 函数。这种方法有用,但是在将 C++ 类包装为 Ruby 对象时,这种方法不够好用。要包装 C++ 类,仍需要使用 define_class 方法,除非现在您使用 C++ 类类型对它进行了 “模板化” 。清单 13 中的代码将 C++ 类包装为 Ruby 类型。
清单 13. 将 C++ 类包装为 Ruby 类型

class cppType {
  public:
   void print(String args) {
    std::cout << args.str() << endl;
   }
};
Class rb_cTest =
    define_class<cppType>("Test")
     .define_method("print", &cppType::print);

注意,如前所述,对 define_class 进行了模板化。尽管这种方法并不是适合所有此类。下面是您试图实例化类型 Test 的对象时,Ruby 解释器的记录:

>> t = Test.new
TypeError: allocator undefined for Test
 from (irb):3:in `new'
 from (irb):3

刚刚发生了什么事?您需要将构造函数显式地绑定到 Ruby 类型。(这是 Rice 的怪异之处之一。)Rice 为您提供了 define_constructor 方法来关联 C++ 类型的构造函数。您还需要包含头文件 Constructor.hpp。注意,即使在您的代码中没有显式构造函数,您也必须这样做。清单 14 提供了示例代码。
清单 14. 将 C++ 构造函数与 Ruby 类型关联起来

#include "rice/Constructor.hpp"
#include "rice/String.hpp"
class cppType {
  public:
  void print(String args) {
    std::cout << args.str() << endl;
   }
  };

Class rb_cTest =
    define_class<cppType>("Test")
     .define_constructor(Constructor<cppType>())
    .define_method("print", &cppType::print);

还可以将构造函数与使用 define_constructor 方法的参数列表关联起来。Rice 进行此操作的方法是为模板列表添加参数类型。例如,如果 cppType 有一个接收整数的构造函数,那么您必须将 define_constructor 作为 define_constructor(Constructor<cppType, int>()) 进行调用。关于此处的一条警告:Ruby 类型没有多个构造函数。因此,如果您有具有多个构造函数的 C++ 类型,并使用 define_constructor 将它们关联起来,那么从 Ruby 环境的角度讲,您可以像源代码最后一个 define_constructor 定义的那样,初始化具有(或没有)参数的类型。清单 15 解释了刚刚讨论的所有内容。
清单 15. 将构造函数与参数关联起来

class cppType {
  public:
   cppType(int m) {
    std::cout << m << std::endl;
   }
   cppType(Array a) {
    std::cout << a.size() << std::endl;
   }
   void print(String args) {
    std::cout << args.str() << endl;
   }
  };
Class rb_cTest =
    define_class<cppType>("Test")
     .define_constructor(Constructor<cppType, int>())
     .define_constructor(Constructor<cppType, Array>())
     .define_method("print", &cppType::print);

下面是来自 Ruby 环境的记录。注意,最后关联的构造函数是 Ruby 理解的构造函数:

>> t = Test.new 2
TypeError: wrong argument type Fixnum (expected Array)
 from (irb):2:in `initialize'
 from (irb):2:in `new'
 from (irb):2
>> t = Test.new [1, 2]
2
=> #<Test:0x10d52cf48>

将新 Ruby 类型定义为模块的一部分

从 C++ 定义新 Ruby 模块可归结为调用 define_module。要定义仅作为所述模块一部分的类,请使用 define_class_under 而不是常用的 define_class 方法。define_class_under 的第一个参数是模块对象。根据 清单 14,如果您打算将 cppType 定义为名为 types 的 Ruby 模块的一部分,清单 16 显示了如何进行此操作。
清单 16. 将类型声明为模块的一部分

#include "rice/Constructor.hpp"
#include "rice/String.hpp"
class cppType {
  public:
  void print(String args) {
    std::cout << args.str() << endl;
   }
  };

Module rb_cModule = define_module("Types");
Class rb_cTest =
    define_class_under<cppType>(rb_cModule, "Test")
     .define_constructor(Constructor<cppType>())
    .define_method("print", &cppType::print);

下面是在 Ruby 中使用相同声明的方法:

>> include Types
=> Object
>> y = Types::Test.new [1, 1, 1]
3
=> #<Types::Test:0x1058efbd8>

注意,在 Ruby 中,模块名称和类名称必须以大写字母开头。如果您将模块命名为 types 而不是 Types,Rice 不会出错。

使用 C++ 代码创建 Ruby 结构

您在 Ruby 中使用 struct 构造函数来快速创建样本 Ruby 类。清单 17 显示了使用名为 a、ab 和 aab 的三个变量创建类型 NewClass 的新类的方法。
清单 17. 使用 Ruby Struct 创建新类

>> NewClass = Struct.new(:a, :ab, :aab)
=> NewClass
>> NewClass.class
=> Class
>> a = NewClass.new
=> #<struct NewClass a=nil, ab=nil, aab=nil>
>> a.a = 1
=> 1
>> a.ab = "test"
=> "test"
>> a.aab = 2.33
=> 2.33
>> a
=> #<struct NewClass a=1, ab="test", aab=2.33>
>> a.a.class
=> Fixnum
>> a.ab.class
=> String
>> a.aab.class
=> Float

要在 C++ 中进行 清单 17 的等效编码,您需要使用头文件 rice/Struct.hpp 中声明的 define_struct( ) API。此 API 返回 Rice::Struct。您将此 struct 创建的 Ruby 类与该类所属的模块关联起来。这是 initialize 方法的目的。使用 define_member 函数调用定义各个类成员。注意,您已经创建了一个新的 Ruby 类型,可惜您没有将任何 C++ 类型或函数与它关联起来。下面是创建名为 NewClass 的类的方法:

#include "rice/Struct.hpp"
…
Module rb1 = define_module("Types");
define_struct().
    define_member("a").
    define_member("ab").
    define_member("aab").
    initialize(rb1, "NewClass");

结束语

本文介绍了一些背景知识:使用 C++ 代码创建 Ruby 对象,将 C 样式的函数作为 Ruby 对象方法进行关联,在 Ruby 和 C++ 之间转换数据类型,创建实例变量,以及将 C++ 类包装为 Ruby 类型。您可以使用 ruby.h 头文件和 libruby 实现所有这些操作,但是您需要编写大量样板代码来结束所有操作。Rice 使这些工作变得更加简单。在这里,祝您使用 C++ 针对 Ruby 环境编写新扩展愉快! world!

(0)

相关推荐

  • Ruby on Rails下的图像处理入门教程

    图像可以说是任何应用至关重要的一部分.从社交网络到一个简单的Bug追踪器,图像都扮演着重要的角色.然而管理图像并不是一件容易的事情,需要提前耗费大量的时间精力去计划. 本文演示了如何在Rail中实现这一目标.如何处理你的图像以及在后台创建多个版本?如何通过压缩图像又不损图像质量,以此来提高页面性能?这些且听本文一一道来. 入门 本文教程是运行于Rails 4.2,通过MongoDb数据库和HAML呈现视图.不过本文所展示的片段应该兼容任何Rails版本(尽管有些配置差异). 布置舞台 Image

  • 优化Ruby代码使程序运行速度提高的例子

    这篇文章主要介绍了我是如何把ruby gem contracts.ruby速度提升10倍的. contracts.ruby在我项目里用来添加代码合约(code contracts)到Ruby中.看起来差不多是这样的: Contract Num, Num => Num def add(a, b) a + b end 只要add方法被调用,参数和返回值都会被检查. 20秒 本周末,我对该库进行了测试,发现其性能非常糟: 这是在随机输入下,运行1000次以后的结果. 所以,当给一个函数加入合约功能后,

  • 详解Ruby中范围的概念

    范围无处不在:1月至12月,0至9日,50至67行,依此类推. Ruby支持范围,并允许我们使用多种方式的范围: 作为序列范围 作为条件范围 作为区间范围 作为序列范围: 首先,也许是最自然的使用范围来表达序列.序列有一个起点,一个终点和序列中的连续值的方法来生产. Ruby创建'' ..''和'' ...''范围内运算符使用这些序列.这两个点的形式建立一个包容性的范围,而三个点的形式创建了一个范围,不包括指定的高值. (1..5) #==> 1, 2, 3, 4, 5 (1...5) #==>

  • 使用C++来编写Ruby程序扩展的教程

    Ruby 最酷的功能之一就是使用 C/C++ 定义的应用程序编程接口 (API) 扩展它.Ruby 提供了 C 头文件 ruby.h,它随附提供了许多功能,可使用这些功能创建 Ruby 类.模块和更多内容.除了头文件,Ruby 还提供了其他几个高层抽象来扩展基于本地 ruby.h 构建的 Ruby,本文要介绍的是 Ruby Interface for C++ Extensions 或 Rice. 创建 Ruby 扩展 在进行任何 Ruby 的 C API 或 Rice 扩展前,我想明确地介绍一下

  • Ruby中使用SWIG编写ruby扩展模块实例

    在使用ruby/rails的过程中,确实发现有时性能不尽人意,如生成一个拥有600项的item的3层树形结构目录要花去20ms,为提高性能在学习用c/c++写ruby模块的过程中,认识了swig,rubyInline等一系列帮助编写c/c++来提升ruby性能的辅助工具. rubyInline用于内嵌c/c++程序,简单快捷,swig则帮助我们更容易地用c/c++写出独立的ruby模块. swig的入门使用方法 目标:用swig/c++编写一个ruby模块Test,并提供add方法作加法运算.

  • C++编写高性能服务器实例教程

    我将展示如何使用现代C++编写一个Echo服务器,相当于分布式系统开发中的"Hello World".这个服务器会将接收的消息直接返回.我们同时需要一个可以向我们的服务器发动消息的客户端,在这里可以发现客户端的源码. Wangle是一个用来搭建事件驱动的现代异步C++服务的C/S应用框架.Wangle最基本的抽象概念就是Pipeline(管线).能够理解这种抽象,将会很容易写出各种复杂的现代C++服务,另一个重要的概念是Service(服务),其可以看作一种更高级的Pipeline,不

  • 利用Ruby的SOAP4R编写SOAP服务器的教程

     什么是SOAP ? 简单对象访问协议(SOAP)是一个跨平台和语言无关的,基于XML的RPC协议,通常(但不一定)是HTTP. 它使用XML来编码信息使远程过程调用,HTTP在网络上从客户机到服务器来传输信息,反之亦然. SOAP有几个优势超过其他技术,如COM,CORBA等为例,其相对廉价的部署和调试成本,它的可扩展性和易于使用,存在几种不同的语言和平台实现. 请参阅出简单的教程了解 SOAP 本教程将熟悉SOAP实现Ruby(SOAP4R).这是一个基本的教程,所以如果需要深入细节,那么需

  • 为Python的web框架编写前端模版的教程

    虽然我们跑通了一个最简单的MVC,但是页面效果肯定不会让人满意. 对于复杂的HTML前端页面来说,我们需要一套基础的CSS框架来完成页面布局和基本样式.另外,jQuery作为操作DOM的JavaScript库也必不可少. 从零开始写CSS不如直接从一个已有的功能完善的CSS框架开始.有很多CSS框架可供选择.我们这次选择uikit这个强大的CSS框架.它具备完善的响应式布局,漂亮的UI,以及丰富的HTML组件,让我们能轻松设计出美观而简洁的页面. 可以从uikit首页下载打包的资源文件. 所有的

  • 专门为初学者编写的正则表达式入门教程

    这是一篇翻译文章.我学过很多次正则表达式,总是学了忘,忘了学,一到用的时候还是只能靠搜索引擎. 这回看到这个正则教程,感觉非常惊喜.尝试翻译了一遍,译得不好,大家可以看原文,很容易理解. 原文地址:https://refrf.shreyasminocha.me/ 1 介绍 正则表达式允许定义一种模式,并通过这种模式针对字符串执行对应的操作.与模式匹配的子字符串称为"匹配". 正则表达式是定义搜索模式的一串字符. 正则表达式主要用在如下场景: 输入验证 查找替换操作 高级字符串操作 文件

  • Windows下ruby语言安装教程

    第一步:下载安装文件 官网下载地址:http://rubyinstaller.org/downloads 第二步: 双击安装 在安装的时候,请勾选Add Ruby executables to your PATH这个选项,添加环境变量,不然以后使用编译软件的时候会提示找不到ruby环境 第三步: 检测安装完成否

  • ruby 数组使用教程

    你通过在方括号里列出元素并用逗号将它们相互隔开来创建一个数组. Ruby的数组可以适应不同的对象类型 ruby> ary = [1, 2, "3"]    [1, 2, "3"] 就像前面提到的字符串一样.数组也可以相乘或相加 ruby> ary + ["foo", "bar"]    [1, 2, "3", "foo", "bar"] ruby> 

  • 在Python中编写数据库模块的教程

    在一个Web App中,所有数据,包括用户信息.发布的日志.评论等,都存储在数据库中.在awesome-python-app中,我们选择MySQL作为数据库. Web App里面有很多地方都要访问数据库.访问数据库需要创建数据库连接.游标对象,然后执行SQL语句,最后处理异常,清理资源.这些访问数据库的代码如果分散到各个函数中,势必无法维护,也不利于代码复用. 此外,在一个Web App中,有多个用户会同时访问,系统以多进程或多线程模式来处理每个用户的请求.假设以多线程为例,每个线程在访问数据库

  • 仅用50行代码实现一个Python编写的计算器的教程

     简介 在这篇文章中,我将向大家演示怎样向一个通用计算器一样解析并计算一个四则运算表达式.当我们结束的时候,我们将得到一个可以处理诸如 1+2*-(-3+2)/5.6+3样式的表达式的计算器了.当然,你也可以将它拓展的更为强大. 我本意是想提供一个简单有趣的课程来讲解 语法分析 和 正规语法(编译原理内容).同时,介绍一下PlyPlus,这是一个我断断续续改进了好几年的语法解析 接口.作为这个课程的附加产物,我们最后会得到完全可替代eval()的一个安全的四则运算器. 如果你想在自家的电脑上试试

随机推荐