Electronic Joint Business

Solution for E-Business

编程语言

.NET 4.5 的五大最佳特性

.NET 4.5 已经发布了将近一年了,现在有个问题是 .NET 开发者的对于微软最新的发布版,通常只约略知晓一到两个特性,其它则只流于表面,它们最终不过成为 MSDN 上的简单文档说明而已。

如果你问个开发者 .NET 4.5 核心框架中多了哪些新特性,大部分人只会回答 async 和 awaitt。(至少和我打交道的人只提到了上面这些特性。)

确实,要深入了解所有的新特性非常困难。特别是,是否值得去了解这些特性完全取决于你手边的工作。

因此,在这篇文章中,我会介绍我所喜欢的几个 .NET 4.5 核心特性。这只是我个人的偏爱,未必就是你的最爱。不过如果我选的这些话题对不断壮大的 .NET 社区能有所帮助,那我就心满意足了。

注意: 本文只会涉及与 .NET 核心框架相关的新特性,而不讨论诸如 ASP.NET、 WCF、 WPF、 WWF 等等特性。

>>> 阅读全文

 

, ,

Scala 指南

一种可伸缩语言

Scala 是一种函数对象混合的语言,具有一些强大的优点:

首先,Scala 可编译为 Java 字节码,这意味着它在 JVM 上运行。除了允许继续利用丰富的 Java 开源生态系统之外,Scala 还可以集成到现有的 IT 环境中,无需进行迁移。
其次,Scala 基于 Haskell 和 ML 的函数原则,大量借鉴了 Java 程序员钟爱的面向对象概念。因此,它可以将两个领域的优势混合在一起,从而提供了显著的优点,而且不会失去我们一直依赖的熟悉的技术。
最后,Scala 由 Martin Odersky 开发,他可能是 Java 社区中研究 Pizza 和 GJ 语言的最著名的人,GJ 是 Java 5 泛型的工作原型。而且,它给人一种 “严肃” 的感觉;该语言并不是一时兴起而创建的,它也不会以同样的方式被抛弃。
Scala 的名称表明,它还是一种高度可伸缩 的语言。我将在本系列的后续文章中介绍有关这一特性的更多信息。

下载并安装 Scala

可以从 Scala 主页 下载 Scala 包。截止到撰写本文时,最新的发行版是 2.6.1-final。它可以在 Java 安装程序版本 RPM 和 Debian 软件包 gzip/bz2/zip 包中获得,可以简单地将其解压到目标目录中,而且可以使用源码 tarball 从头创建。(Debian 用户可以使用 “apt-get install” 直接从 Debian 网站上获得 2.5.0-1 版。2.6 版本具有一些细微的差异,所以建议直接从 Scala 网站下载和安装。)

>>> 阅读全文

 

, , , , ,

C++11 智能指针

文章评价:

许多程序员总避免使用指针,因为一旦处理不当,就会带来很多问题。所以新手程序员都不太喜欢指针。使用指针可能会引发许多问题,例如需确保指针所引用的对象的生命周期、虚引用(dangling references)和内存泄漏。

如果某内存块为多个指针变量所引用,而其中有个变量释放了这片内存,就会造成虚引用(dangling references)。另一种情况是,程序员从堆里申请了内存块,但未进行释放,这时则会造成内存泄漏。

某些资深程序员可能会说,你看我正确地从堆里获得了内存,进行相关操作,最后也正确地释放了内存, 这段代码干净漂亮,为什么还需要智能指针呢?

void Foo( )
{
    int* iPtr = new int[5];
    //manipulate the memory block
    .
    .
    .
    delete[ ] iPtr;    
}

的确,上面的代码目前工作正常,在理想情况下也能正确地释放内存。但如果考虑运行代码时的实际环境,在内存分配与释放之间存在着许多指令,这些指令可以做一些乱七八糟的事,比如访问无效的内存位置、除零错,甚至可能你的同事修改了你的代码,添加某种条件下的 return 语句。

>>> 阅读全文

 

,

Spark,一种快速数据分析替代方案

Spark 是一种与 Hadoop 相似的开源集群计算环境,但是两者之间还存在一些不同之处,这些有用的不同之处使 Spark 在某些工作负载方面表现得更加优越,换句话说,Spark 启用了内存分布数据集,除了能够提供交互式查询外,它还可以优化迭代工作负载。

Spark 是在 Scala 语言中实现的,它将 Scala 用作其应用程序框架。与 Hadoop 不同,Spark 和 Scala 能够紧密集成,其中的 Scala 可以像操作本地集合对象一样轻松地操作分布式数据集。

尽管创建 Spark 是为了支持分布式数据集上的迭代作业,但是实际上它是对 Hadoop 的补充,可以在 Hadoop 文件系统中并行运行。通过名为 Mesos 的第三方集群框架可以支持此行为。Spark 由加州大学伯克利分校 AMP 实验室 (Algorithms, Machines, and People Lab) 开发,可用来构建大型的、低延迟的数据分析应用程序。

Spark 集群计算架构
虽然 Spark 与 Hadoop 有相似之处,但它提供了具有有用差异的一个新的集群计算框架。首先,Spark 是为集群计算中的特定类型的工作负载而设计,即那些在并行操作之间重用工作数据集(比如机器学习算法)的工作负载。为了优化这些类型的工作负载,Spark 引进了内存集群计算的概念,可在内存集群计算中将数据集缓存在内存中,以缩短访问延迟。

Spark 还引进了名为 弹性分布式数据集 (RDD) 的抽象。RDD 是分布在一组节点中的只读对象集合。这些集合是弹性的,如果数据集一部分丢失,则可以对它们进行重建。重建部分数据集的过程依赖于容错机制,该机制可以维护 “血统”(即充许基于数据衍生过程重建部分数据集的信息)。RDD 被表示为一个 Scala 对象,并且可以从文件中创建它;一个并行化的切片(遍布于节点之间);另一个 RDD 的转换形式;并且最终会彻底改变现有 RDD 的持久性,比如请求缓存在内存中。

>>> 阅读全文

 

, , , , ,

通过 C# 使用 ZeroMQ (二) 多段式消息,JSON 和同步发布/订阅模式

文章评价:

这是该系列文章的第二部分。在这部分文章中,我们会讨论 ZeroMQ 所能发送和接收的两大类消息,并且如何用 JSON 来格式化消息。我们还会学习 如何使用轮询 (polling) 机制。在文章的最后,我们举例说明什么是同步发布/订阅模式。

ZeroMQ消息
ZeroMQ 套接字可以发送或者接受单段式消息(single-part)或多段式消息(multi-part)。

单段式消息
所谓单段式消息,就是一个消息只包含一帧。所谓帧是一个字节数组,其长度可以零以上的数值。在 ZeroMQ 参考手册中,帧也被叫做“消息段 message parts”。

举个单段式消息的例子,就比如说上一篇文章中我们发送/接收的所有消息都是单段式消息。在发送和接收的操作中,我们使用的是字符串,而字符串实质也是一个字节数组。

>>> 阅读全文

 

, , , ,

ADA 语言编程 (一)

文章评价:
在上个世纪 70 年代,美国国防部 (DoD) 发现其所属的嵌入式计算机系统项目中,使用的编程语言数量逐日增多,其中很多语言十分陈旧或者需要依赖于硬件,而且没有一种语言支持安全的模块化编程,对此 DoD 感到十分担心。为此,DoD 在 1975 年成立了高级语言工作组 (HOLWG),它的使命是就是寻找或者创造某种适合国防部需要的编程语言,以便减少现有编程语言数量。该小组最终的工作成果就是 Ada 语言。由此,这类项目中使用的高级编程语言的数量从 1983 年的 450 种编程语言,减少到 37 种(1996年)。

高级语言工作组精心设计了 Steelman 语言规范,这是一系列用来述明编程语言需要满足哪些要求的文件。许多既有的语言按次规范进行了审查,但最终(1977年) 团队得出结论:没有现有的语言符合规格。

最终 HOLWG 提出了开发一种新的编程语言的建议,并且雇佣了四个承包商分别进行有关方案开发,代号分别是红 (Benjamin Brosgol 所领导的Intermetrics)、 绿 (Jean Ichbiah 所领导的 CII Honeywell Bull)、 蓝 (John Goodenough 所领导的 SofTech) 和黄 (Jay Spitzen 所领导的SRI International。1978 年 4 月公开审查之后 红和绿这两个方案进入了下一阶段。在 1979 年 5 月,最终选择了 Jean Ichbiah 所领导的 CII Honeywell Bull 所实现的“绿”方案,并定名为 Ada — — 这是为了纪念 Augusta-Ada-Lovelace 伯爵夫人。该方案受到了上世纪 70 年代 Ichbiah 和他的小组所开发 LIS 编程语言的影响。

由于 Ada 以安全性为第一(safety-critical) 的特性 ,因而 Ada 除了在军事上得以应用,也应用到了那些软件 bug 会产生严重后果的商业项目中,例如航空和空中交通管制、 商业火箭 (例如阿丽亚娜 4 和 5),卫星和其他空间系统、 铁路运输和银行。例如,波音 777 的线传飞控系统软件就是 Ada 编写的。加拿大的自动化空中交通系统是由 100 万行的 Ada (源代码计数) 构成的。得益于 Ada 的高级分布式处理、 分布式的 Ada 数据库和面向对象的设计,Ada 也用在其他的空中交通系统,如英国的下一代 iFACTS 空中交通管制系统是用 SPARK Ada 设计和开发的, 同时它也运用于法国 TGV 高速铁路系统中的 TVM in-cab 信号系统,以及巴黎、 伦敦、 香港和纽约地铁信号系统。

标准
1983 年该语言成为了 ANSI 标准 (ANSI/MIL-STD 1815A), 该版本未做更多的修改就在 1987 年成为了 ISO 标准 (ISO-8652:1987)。该版本就是我们知道的 Ada 83,(按 ANSI 接受为标准的时间),有时也按 ISO 接受为标准的日期称为 Ada 87。

>>> 阅读全文

 

, , , ,

通过 C# 使用 ZeroMQ (一) ZeroMQ 通讯模式

文章评价:
ZeroMQ (也拼写作 ØMQ、 0MQ 或 ZMQ) 是个非常轻量级的开源消息队列软件。它没有独立的服务器,消息直接从一个应用程序被发送到另一个应用程序。ZeroMQ 的学习和应用也非常简单,它只有一个 C++ 编写成的单个库文件 libzmq.dll, 可以链接到任何应用程序中。如果要在 .NET 环境中使用,我们需要用到一个 C# 编写的名为 clrzmq.dll 包装库。

ZeroMQ 可以在 Windows、 OS X 和 Linux 等多种操作系统上运行, C、 C++、 C#、 Java、 Python 等语言都可以编写 ZeroMQ 应用程序…这使得不同平台上的不同应用程序之间可以相互通讯。

ZeroMQ 的核心
ZeroMQ 的主要部分是套接字 Socket,不过它并未直接使用传统的套接字,而是在传统的套接字 API上提供了一个抽象层,这让用户从复杂和重复的编程任务中解脱出来。ZeroMQ 支持多种类型的套接字 (类型被定义为套接字自身的一个属性)。发送端和接收端的套接字类型组合造就了多种不同的通信模式,本文的后面我们会涉及到这一部分。

异步通讯
ZeroMQ 提供了异步通讯方式,这意味着即使在设置或关闭套接字连接、重新连接或进行消息传递的时候,应用程序都不会被阻塞。与程序正常任务处理并行的,这些操作由 ZeroMQ 自身在后台线程中进行管理。在需要时,ZeroMQ 会自动将消息 (无论是发件端或接受端) 放入队列中, 这一过程相当智能,消息会被推送到离接收端尽可能近的队列中。

传输协议
ZeroMQ 支持四类传输协议。每种传输协议由地址字符串来定义,该字符串由两部分组成:transport://endpoint。传输(transport) 部分指定了所使用的底层传输协议,端点(endpoint) 部分的格式则随着使用的协议而有所不同,具体如下:

>>> 阅读全文

 

, , , , ,

LLVM 简介

本文将讨论让 LLVM 成型的一些设计策略。LLVM 是个一揽子项目(a umbrella project),用于支持和开发一系列紧密相关的底层工具组件(例如汇编器、编译器、调试器等等),从设计之初,LLVM 就考虑兼容现在广泛使用的 Unix 系统工具。 起初 “LLVM” 这个名字是个缩写,现在则是这个一揽子项目的品牌。虽然 LLVM 提供了独特的功能,并因其强大的工具而闻名(例如 Clang 编译器, 一个比 GCC 更强大的 C/C++/Objective-C 编译器),但真正让 LLVM 有别于其他编译器的地方是在其内部架构。

从诞生之初(2000年12月),LLVM 就被设计成一组具有定义良好接口的、可重用的库。当时,开源的编程语言的实现通常按专用工具来设计的,一般只能不可分割地运行(monolithic executables)。比方说,很难从类似 GCC 这样的静态编译器中将解析器分离出来用在静态分析或者重构。而脚本语言大多都提供了将运行时或解释器嵌入大型应用的方法,该运行时也是一个完整的、不可分割的可以被嵌入或者单独使用的代码块。不同语言实现项目之间根本无法重用部件,代码也很难共享。

除了编译器本身的组成外,围绕流行语言的社区往往呈现出很强的两极化倾向:有些实现提供了传统的静态编译器(如 GCC, Free Pascal 和 FreeBasic), 而有的实现则以解释器或JIT(Just-In-Time)编译器的形式提供了运行时编译器。但是很少有实现同时提供两者,即使有的话,两者也极少共享代码。

过去十多年中,LLVM 慢慢改变了这种困境。现在,LLVM 被作为一个通用的基础框架实现了很多静态编译或运行时编译的语言(例如,GCC 支持的语言族、Java、.NET、Python、Ruby、Scheme、Haskell、D语言以及许多的知名度稍低的语言)。它也取代了很多专用编译器,像苹果公司 OpenGL 栈中的运行时特化引擎(runtime specialization engine)以及 Adobe 公司 After Effects 产品中的图形处理库。最后,LLVM 也用来创建各种新产品,最出名的应该算是 OpenCL GPU 编程语言和运行时了。

传统编译器设计简介
传统的静态编译器设计(如大多数 C 编译器)中采用的最普遍的方案就是三阶段编译,该设计主要由三个组件组成,分别是前端、优化器和后端(如图1-1所示)。前端负责解析源代码,检查错误,并构建语言相关的抽象语法树(AST)。AST 可根据情况转换为便于优化的新表示方式, 优化器和后端针对这种新表示而运行。

>>> 阅读全文

 

, , , , , ,

.NET 4.5 并行之新特性

原文

随着 .NET 4 和 Visual Studio 2010 的发布,微软为并行提供相当广泛的支持,包括: 任务并行库 (TPL)、 并行 LINQ (PLINQ)、 新的同步和协调原语集合 (如 ConcurrentDictionary),为处理并行工作负荷而改进的线程池、 新的调试器窗口、 新的并发可视化工具,等等。自那时起,微软一直致力于埋头开发新的 .NET 4.5 和 Visual Studio 11。这是对 Visual Studio 11 开发者预览的一瞥。

更好的性能

任务并行库

More and more, TPL is becoming the foundation for all parallelism, concurrency, and asynchrony in the .NET Framework. That means it needs to be fast… really fast. Performance in .NET 4 is already good, but a lot of effort was spent in this release improving the performance of TPL, such that just by upgrading to .NET 4, important workloads will just get faster, with no code changes or even recompilation required. As just one example, consider a long chain of tasks, with one task continuing off another. We want to time how long it takes to set up this chain:

>>> 阅读全文

 

, , ,

阅读 Grub 源代码 (一) X86 汇编及 GAS 语法

文章评价:

简介
本文的例子采用 AT&T 汇编语法编写,并用 GNU AS 编译的。该语法的主要优点是它与 GCC 内嵌汇编保持着语法兼容性。不过,这并不是表示 X86 操作符唯一语法。例如,NASM、MASM 就使用了完全不同的语法来表示助记符,操作数和寻址模式及其他高级汇编特性。 AT&T 的语法是类 Unix 系统上的标准,但其他使用 Intel 语法的汇编器,比如 GAS 本身,两种语法都可以接受。

GAS 是 GNU 项目的一部分,它具有可以自由使用、适用于多种操作系统、和 GNC 编译器 (gcc) 和 GNU 链接器 (ld) 等编程工具接口良好等等特点。

如果使用安装了 Linux 操作系统的电脑,通常默认就安装了 GAS。如果使用的是 Windows 操作系统,则可以安装 Cygwin 或者 MinGW 来获得 GAS 和其他编程工具。

GAS 指令一般都采用“ 助记符 源,目的地 ”的格式。例如,下面的 mov 指令:

>>> 阅读全文

 

, , , , , ,

Python编写的强大的通用解析器

Spark 是一种用 Python 编写的强大的、通用的解析器/编译器框架。在某些方面,Spark 所提供的比 SimpleParse 或其它 Python 解析器要多得多。不过由于它完全是用 Python 编写的,所以速度也会比较慢。

我将在本文中继续介绍一些解析的基本概念,并对 Spark 模块进行了讨论。解析框架是一个内容丰富的主题,它值得多花时间去全面了解;这篇文章为读者和我自己都开了一个好头。

在日常的编程中,我经常需要标识存在于文本文档中的部件和结构,这些文档包括:日志文件、配置文件、定界的数据以及格式更自由的(但还是半结构化的)报表格式。所有这些文档都拥有它们自己的“小语言”,用于规定什么能够出现在文档内。我编写这些非正式解析任务的程序的方法总是有点象大杂烩,其中包括定制状态机、正则表达式以及上下文驱动的字符串测试。这些程序中的模式大概总是这样:“读一些文本,弄清是否可以用它来做些什么,然后可能再多读一些文本,一直尝试下去。”

解析器将文档中部件和结构的描述提炼成简明、清晰和说明性的规则,确定由什么组成文档。大多数正式的解析器都使用扩展巴科斯范式(Extended Backus-Naur Form,EBNF)上的变体来描述它们所描述的语言的“语法”。基本上,EBNF 语法对您可能在文档中找到的部件赋予名称;另外,较大的部件通常由较小的部件组成。小部件在较大的部件中出现的频率和顺序由操作符指定。

举例来说,清单 1 是 EBNF 语法 typographify.def,我们在 SimpleParse 那篇文章中见到过这个语法(其它工具运行的方式稍有不同):
  

>>> 阅读全文

 

, , , , , ,

C++11 一览 (二)

简介
在本系列文章中的第一部分,我们介绍了 C++11 的一些特性。在本篇文章,我们会继续介绍其它特性。

Strongly typed enums:

As quoted by Stroustrup “C enumerations constitute a curiously half-baked concept” and very few modifications are done to rectify there shortfalls resulting in manifestation of silent behavioral changes. Let’s check an example to support this,

namespace DefenceNetwork
{
   namespace WeatherMonitor
   {
     enum CycloneWarningLevels
     {
        GREY, // Medium winds
        RED, // High speed winds
        YELLOW, // Very High speed winds
        GREEN, // Severe Cyclone
        BLUE // Very Severe Cyclone
     };
   }
   namespace ThreatMonitor
   {
     enum AlertConditions
     {
        BLUE, // Lowest readiness
        GREEN, // Increased intelligence watch
        YELLOW, // Increase in force readiness
        RED, // Next step to nuclear war
        GREY // Nuclear war is imminent
     };
   }
 }
using namespace DefenceNetwork;
void SetDEFCONLevel(int value)
{
   // ….before setting level …lets check the weather once
   using namespace WeatherMonitor;
   // ……
   // …. Ok all is fine! Lets go ahead with setting DEFCON level
   if(value >= RED)
   {
      cout<<"Nuclear war is imminent…All Missiles GO…GO…GO"<<endl;
   }
   // Here, the confusion is AlertConditions::GREEN and CycloneWarningLevels::RED are same
   // as we messed up by putting ‘using namespace WeatherMonitor’.
 }
void main()
{
   using namespace ThreatMonitor;
   // Set level to Increase intelligence watch
   SetDEFCONLevel( AlertConditions::GREEN );
   
  // Oh no… what have I done …How the war is started
  // Hope in reality they have coded better than this…but hey can we take a chance !!!
}

The problem with the enums so far is that

1. They can be silently converted to int.
In the above example SetDEFCONLevel( ) method need int and when we pass an
enumerator it happily accepted.

>>> 阅读全文

 

,

C++11 一览 (一)

C++0x 现在成为了一个正式的标准,将以下称为 C++11。在2011年,它被批准成为ISO C++。本文的目的是鸟瞰大部分的 C++11 功能,并对那些已经添加进 VS2010 的功能做出深刻的分析。本文可以作为一个平台让您对个别功能开始进行全面的研究。

背景
几乎花了一个世纪的时间,查尔斯 · 巴贝奇(Charles Babbage)的分析机(Difference Engine)才演变成强大的计算机。在四十年代,受限于当时计算机的运算速度和内存,只能使用汇编级别的语言。十年后,事情开始有所变化,从 1950 年到 1970 年期间绽放出许多编程语言,其中许多一些延续到今天。

1979 年,为贝尔实验室工作的 Bjarne Stroustrup 开始着手加强”C”语言,首先添加了类,然后是虚函数、 运算符重载,多重继承、 模板和异常处理,以及其它功能。起初他管这个语言叫“带类的C -C with Classes”。在1983年更名为 c++ (+ + 可能说它是 C 的递增) 。

C++ 大事件/演化时间表如下:

  • 1983 – 第一个商业版本的 C++ 编译器
  • 1998 – C++ 标准委员会开始标准化 C++ [C++98]
  • 2003 – 推出不带任何新功能的补丁包 [C++03]
  • 2005 – 发布了“库技术报告- Library Technical Report 简称 TR1”
  • 2011 – 引入大量的功能并增强 C++ 标准库

我们可以看出,这个变化是最大的一次(好吧,添加 STL 也可以算是大变化)。

>>> 阅读全文

 

, , ,

用 autoconf 和 automake 生成 makefile

文章评价:
在 Unix/Linux 上编写程序的人大都遇到过 Makefile,尤其是使用 C/C++ 语言的开发者。用 make 来开发和编译程式的确很方便,可是要编写一个 Makefile 就不那么简单了。而且介绍 Makefile 的资料并不多,GNU Make 那份打印出来要几百页的文件,光看完序言就是花不少时间,难怪许多人谈 Unix 编程而色变。

本文将介绍如何利用 GNU Autoconf 及 Automake 这两套软件来帮助我们“自动”生成 Makefile 文件,让开发出来的软件可以像 Apache,MySQL 等常见应用一样,只要输入 “ ./configure ”, “ make ”,“ make install” 就可以把编译结果安装到系统中。

如果您有心开发开源软件,或主要是在 Unix/Linux 系统下编写程序。希望这份介绍文件能帮助您轻松地进入 Unix/Linux 编程的殿堂。

简介
Makefile 基本上就是“目标”(target), “依赖”(dependencies) 和“动作”三者所组成的一连串规则。而 make 就会根据 Makefile 的规则来决定如何编译 (compile) 和链接 (link) 程序。实际上,make 可做的不只是编译和链接程序,例如 FreeBSD 的 port collection 中,Makefile 可以自动去下载原始程式包,解压缩 (extract) ,打补丁 (patch),然后运行设定并编译,最后安装至系统中。

Makefile 基本构造虽然简单,但如能妥善运用这些规则就也可以玩出许多不同的花样。却也因此,许多人刚开始学习写 Makefile 时会感到没有规范可循,每个人写出来的 Makefile 长得都不太一样,不知道从何下手,而且常常会受限于自己的开发环境,只要环境参数不同或路径被修改一下,可能就需要跟着修改 Makefile。虽然有 GNU Makefile Conventions (GNU Makefile 惯例) 订出一些使用 GNU 程序设计时撰写 Makefile 的一些标准和规范,但是内容很长而且很复杂, 并且经常调整。为了减轻程序设计师维护 Makefile 的负担, GNU 推出了 Automake 这个工具。

>>> 阅读全文

 

, , , ,

初识 Sinatra (四)

文章评价:

这是本系列文章的最后一部分。本教程的目标是带领从未用过 Sinatra 的用户从头开始创建一个应用,并将之最后发布。

到前三部分结束时,我们已经完成了 To Do List 应用的大部分功能。在本系列的最后一部分,我们将先看看如何用 Sass 来编写 CSS ,之后将本应用发布到 Heroku 云服务上。

Logo
每个上点档次的应用都应有一个 logo,所以我创建了一个简单的“tick” logo,你可以在 Github 上找到。我们用它来作为本应用的头条的背景图片。打开 Styles.css 并添加下列 CSS:

h1.logo{
  font-family: helvetica,arial,sans-serif;
  font-size: 24px;
  color: white;
  padding: 64px 0 0;
  margin: 0 auto;
  text-transform: uppercase;
  text-align: center;
  font-weight: normal;
  letter-spacing: 0.3em;
  background: transparent url(/logo.png) 50% 0 no-repeat;
  }

(注意 – 也许你还发现,你需要为 layout.slim 的 h1 元素添加一个 ‘logo’ 类,这段 CSS 才会起作用。)

>>> 阅读全文

 

, , , , , , , ,

Previous Posts Next posts