前言
正准备写一篇关于推荐用C作为入门语言的文章《关于程序员的入门语言》,其中一个论点是:
C语言的语法跟Java、JavaScript、PHP等很像,甚至可以说是后者们的基础。
因为要贴出代码,篇幅较长,所以将其独立成一篇,正是此文。
〇、功能要求描述
要求:
对给出的一个字符串,将其中的空格去掉,大写字母变小写。
例如,”HELLO, world.”(13个字节)的转换结果是”hello,world.”(12字节)。
一、几种语言的代码实现
以下是C、Java、JavaScript、PHP、Python等的实现代码。
注:全部代码在 macOS 10.13 上运行通过。
C语言实现
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
int convert(char *text, char* buffer, int bufferSize) {
char *p;
int count = 0;
for (p = text; *p != '\0'; p++) {
if (*p != ' ') {
buffer[count++] = tolower(*p);
if (count >= bufferSize) {
break;
}
}
}
return count;
}
int main(int argc, char *argv[]) {
char *target = "HELLO, world.";
char *buffer;
#define BUF_SIZE 128
buffer = malloc(BUF_SIZE); // 假定缓冲区大小足够
// convert
int size = convert(target, buffer, BUF_SIZE - 1); // 减1以预留给 '\0'
buffer[size] = '\0'; // 须显式地结束字符串
// output
printf("%s\n", buffer);
printf("%d\n", size);
free(buffer); // 释放内存
return 0;
}
C99标准
Java实现
class Convert {
public static String convert(String text) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < text.length(); i++) {
char c = text.charAt(i);
if (c != ' ') {
c = Character.toLowerCase(c);
sb.append(c);
}
}
return sb.toString();
}
public static void main(String[] args) {
String target = "HELLO, world.";
// convert
String output = convert(target);
// output
System.out.println(output);
System.out.println(output.length());
}
}
Java 8
JavaScript实现
function convert(text) {
var buffer = '';
var i;
var c;
for (i = 0; i < text.length; i++) {
c = text[i];
if (c != ' ') {
buffer += c.toLowerCase();
}
}
return buffer;
}
var target = "HELLO, world.";
// convert
var output = convert(target);
// output
console.log(output);
console.log(output.length);
Node.js v9.4.0
注:后文有更简洁的实现
PHP实现
function convert($text) {
$buffer = "";
for ($i = 0; $i < strlen($text); $i++) {
$c = $text[$i];
if ($c != ' ') {
$buffer = $buffer . $c;
}
}
return $buffer;
}
$target = "HELLO, world.";
// convert
$output = convert($target);
// output
echo $output . "\n";
echo strlen($output);
PHP 7
Python实现
def convert(text):
buffer = ''
for x in text:
if x != ' ':
buffer += x.lower()
return buffer
target = "HELLO, world."
# convert
output = convert(target)
# output
print(output)
print(len(output))
Python 3.6
注:后文有更简洁的实现
二、语言间异同和知识点
通读上述代码,你就会发现,除了Python外,Java、JavaScript和PHP在语法上跟C是很像的,当然也有不同的地方。
当你熟悉了这些异同后,就很容易学习和掌握C之外的几种语言了。
二.1. 相同点
- 字符串都是以类似于数组的形式存在,可以遍历(iterating);
- 定义函数,以及函数的参数、返回值的方式比较接近;
- 条件判断(if、!=等)几乎一样;
- for语法几乎一样;
- C之外的几种语言支持for的变种(如for…in,forEach等),以便更方便地遍历Array, List, Map, Set等;
- 出了for, while, switch等基本上是一样的(Python没有switch);
- 都支持+=赋值方式,几乎都有++运算符(Python没有);
- (除Python外),都是使用花括号({ / } )定义控制类、函数、程序块等的边界,都是使用分号(;)来结束一行语句(JavaScript支持不写分号,具体是否不写分号,就见仁见智了) — 语法何其相似呀。
二.2 不同点
- C和Java是“静态类型语言”(Statically Typed Language),就是说,对于变量、函数参数、函数返回值,你都必须在声明(declaration)时指定它们的类型,编译器在编译时会进行检查。
- 相应地,JavaScript、PHP、Python等则是“动态类型语言”(Dynamically Typed Language);
- JavaScript的变量需要用var来定义,函数参数就不需要了;
- PHP的变量、参数的前面都需要加美元符号($);
- Python类似于PHP,但不需要加美元符号($);
- 概括来说,你可以说C和Java中用的是“变量”(Variables)(一般指在内存中位置相对固定,且有明确类型),而JavaScript、PHP、Python中用的是“名字”(Names)或者“符号”(Symbols),以此区别于“变量”;
- C语言的字符串是char数组或者char指针,C没有专门的“字符串”类型;
- 所以对C对Unicode的支持不够友好,其他几种语言的新版本对Unicode都提供了built-in的支持—-这一点在本文中暂不深入讨论;
- C的char是8-bit的,而Java的char是16-bit的;
- 其他几种语言对字符串都做了或多或少的封装,且基本上要求“字符串一旦生成即再不可变”—-这一点会消耗一定的内存和计算,但因为在一定程度上减少了因为“可变性”(mutability)带来的bugs – 这一点有点Functional Programming(函数式编程)的意味,可独立成篇;
- 因为Java中的字符串是不可变的,所以出于性能考虑,Java提供了StringBuilder – 先开辟一片内存然后通过append来追加,而不是“生成新String然后丢弃旧String”,从而降低了“垃圾回收”(GC)的负担;
- C语言需要程序员管理内存的长度计算、申请、释放,而其他语言则自行管理内存;
- C的例子中,因为buffer是显性malloc出来,然后传给convert函数的,所以convert函数里面需要检查,不能“越界访问”,所以每次填入一个字符就要判断是否越界(count >= bufferSize);
- 其他几种语言,自行管理内存的申请、释放 – 当然,在上述例子中,它们都存在性能较差的问题(Java好一些,因为用到了StringBuilder从而较少了“中间临时字符串”对象的生成和释放);
- 其他几种语言自动释放内存的机制各不相同,本文不深入讨论;
三、总结
看了上面的 代码样例 和 异同点比较,相信(希望😊)你已经对C语言必要性有了深一步的认识。
这里需要指出的有:
- 其他几种语言虽然语法上比C更高级,编程时可以忽略很多细节(更加专注于What to do而不是How to do),但底层中,要做的还是要做的(例如,遍历数组、分配内存、所分配的内存不足时需要重新分配)- 这涉及到性能问题 — 虽然编程时不一定涉及,但思考时需要知道这细节;
- Unicode字符串、字符编解码等归根结底,在内存和磁盘上都是以byte数组、序列的形式存在(一个中文字符以UTF-8编码时一般占3个bytes,有些占4个bytes;内存的操作归根结底也是byte数组 — 学习C可以让你更好地理解内存、指针、引用等,从而为掌握其他语言、对象生命周期等打好坚实的基础;
另外,C语言的编译、链接过程也是值得学习和掌握到一定程度的,因为这对学习Java的Ant、Maven,或者JavaScript的WebPack等是有一定的指导意义的。
四、更优雅的实现
以下是使用JavaScript和Python的更优雅实现(均可以合并为“一行”语句),颇有Functional Programming(函数式编程)的味道—-具体指的是,以“描述需要做的处理”,且可“串联”起来,一般不涉及中间变量等。
- 请跟上面的C例子对比一下,感受一下What to do和How to do的“开发效率”的差异。
JavaScript
var target = "HELLO, world.";
// convert
var output = target.split('')
.filter( c => c != ' ') // 箭头函数。见“参考”中的链接
.map(c => c.toLowerCase())
.join('');
// output
console.log(output);
console.log(output.length);
Node.js v9.4.0
Python
target = "HELLO, world."
# 使用 `List Comprehension` - 见“参考”中的链接
output = [c.lower() for c in target if c != ' ']
output = ''.join(output) # list to string
print(output)
print(len(output))
Python 3.6