Racket 作为众多 Lisp 方言中的一种,是一种可编程的计算机程序设计语言 ,同时也是一个程序设计环境。其也曾一度被列入教科书的素材,但是对于这门小众化语言,是否有必要上手?
作者 | beautiful racket
译者 | 彼得
责编 | 屠敏
出品 | CSDN(ID:CSDNnews)
以下为译文:
作为Lisp语言的新手,我曾经迷失在各种对于Lisp的溢美之词。我甚至不知道其中某些词的含义是什么。比如,人们常常说Lisp是另一场影响深远的启蒙运动。当然,你怎么说它都可以。
对于人们来说,有一个更简单和现实的问题:Lisp对我来说意味着什么?我此前写过文章对此进行了说明。我总结和解答了为什么不了解Lisp语言或特别是Racket语言的人值得花时间去了解它们。
我列出了对于我这个Rocket初学者最重要的9个语言特性。其中,第五个特性是“创建新的编程语言”。这项技术也被称为面向语言编程,或LOP。
在那之后的几年里,面向语言编程已经成为我喜欢Racket的最重要的原因。在此过程中,我也把自己对于Racket的喜欢转化为行动。除了开发语言之外,我还写了在线书籍 《Beautiful Racket》。我认为LOP是一项技术,而Racket是一种实现该技术的工具。在该书中,我对于LOP和Racket都进行了介绍。
在工作中,我开发了一种基于文本的编程语言Pollen。我用它来实现我的在线图书《Practical Typography》和《Beautiful Racket》。在Pollen中,上面的段落会被表示为:
#lang pollen
在链接◊link["why-racket-why-lisp.html#so-really-whats-in-it-for-me-now"]{nine language features},我列出了对于我这个Rocket初学者最重要的9个语言特性。其中,第五个特性是“创建新的编程语言”。这项技术也被称 ◊em{面向语言编程}, 或者 ◊em{LOP}。
另一个例子是brag,一个解析器的生成器(采用lex / yacc样式),它采用BNF语法作为源代码。下面是一个简单的bf语言的示例:
#lang brag
bf-program : (bf-op | bf-loop)*
bf-op : ">" | "<" | "+" | "-" | "." | ","
bf-loop : "[" (bf-op | bf-loop)* "]"
#lang brag
bf-program : (bf-op | bf-loop)*
bf-op : ">" | "<" | "+" | "-" | "." | ","
bf-loop : "[" (bf-op | bf-loop)* "]"
这两种语言都是在Racket中实现的,可以使用通常的Racket解释器运行,也可以在Racket IDE(DrRacket)里运行。
重要问题
虽然这本书已经让成千上万的人开始了他们的LOP之旅,但是,我有时候还是会担心自己会陷入被我批评过的Lisp倡导者们曾经陷入过的窘境之中。
如果LOP真的有这么强大,那么您也就不需要花几天时间来学习我这本教程,我也应该能够很轻松地讲解它。我应该能够回答下面的两个简单的问题:
哪些问题最适合用面向语言的编程解决?
为什么Racket最适合完成开发语言的任务?
第二个问题很容易回答。但是,第一个问题就不太容易回答。很多人也多次问我第一个问题。我经常用Potter Stewart法官说的那句名言来回答:你看到它的时候,你就会知道。这个回答对那些LOP的好奇者来说已经足够了。但是,对那些仍然想要更多实际理由的观望者们,却不会让他们满意。
所以,我将进行一次更有意义的尝试。请记住,我不是计算机科学系的教授。我不会像他们那样讲解编程语言。相反,我将Racket和DSL用在我的实际工作中。我的目地是以对其他实际用户有参考价值的方式来阐述问题。(如果我没有讲清楚,请您点击左边并发送评论。)
简短的回答
面向语言编程实际上是一种接口设计技术。它非常适合于那些需要最少的概念,同时又保持最高的精度的任务。最少的概念=所有的概念都是您允许的,没有可有可无的概念。最高精度=该概念的含义正是您要表达的意思。没有固定的模板。除了LOP,其他的技术都做不到。(您如果着急的化,可以直接阅读适合用LOP完成的这些任务。)
Racket的宏系统使它适用于LOP。宏是定义语言必不可少的工具,它们使编译器代码转换变得很容易。Racket的宏系统比任何其他的都要好。
读到这里,我估计得有一半的读者打算去互联网发布匿名的评论,对我的上述说法提出异议。在你离开这里之前,请注意:无论怎么样,对我来讲都是可以接受的。一方面,LOP和Racket对我的编程效率来说是一个令人难以置信的力量倍增器。我愿意与你分享这些知识,让你也可以从中受益。如果你不接受我的观点,这些工具依旧是我的秘密武器,这样我也可以接受。我还可以比其他99.9%的人更有野心、更有影响地从事更有利可图的工作。
这一切取决于您的选择。
详细的解释
我开始考虑如何回答一个大问题:为什么很难解释面向语言编程的好处?
也许是因为当我们谈论编程语言(简称语言)的时候,我们其实想知道它们是什么编程语言,它们能够做什么。如果简单地这么想,我们就很难看到面向语言编程的价值。
但是,如果我们放大我们的视角,把语言看作是人机界面的一部分,那么我们就更容易看到LOP的优势。
下面我们就这么试一试。
特定领域的语言与通用语言
首先,我们先定义一些术语。面向语言的编程(又称LOP)是通过开发新的编程语言并用新语言编写程序的方法来解某些编程问题。这些新开发的语言通常被称为特定领域的语言或DSL。
顾名思义,特定领域的语言是为了解决特定问题而开发的语言。例如,PostScript,SQL,make,正则表达式,.htaccess和HTML都是特定领域的语言。开发这些语言的初衷不是为了解决所有的问题,恰恰相反,人们开发它们只是为了解决某个特定的问题。
与特定领域的语言相对的是通用语言。常见的通用语言包括C,Pascal,Perl,Java,Python,Ruby,Racket等。为什么这些语言不是DSL?因为人们认为它们能完成通用的计算任务。
实际上,这些通用语言通常都会在某些方面比其他语言做得更好。例如,C擅长系统编程。Perl擅长作为系统管理员的脚本语言。Python适合作为初学者的语言。Racket擅长LOP。每种语言都有它们自己的设计初衷。
特定领域的语言和通用语言也不是一成不变的。例如,Ruby最初是一种通用语言,但通过与Ruby on Rails的关联,它在很大程度上又变成了一种Web应用程序的DSL。另一方面,JavaScript最初是一个用于Web浏览器脚本的DSL。但是现在,它已经像变异的病毒一样,与它最开始的时候大相径庭了。
语言是什么?
如果特定领域的语言和通用语言之间的所有这些都应该被称为语言,那么语言的特征是到底是什么?
我知道你想说:“好吧,你错了。HTML不是一种语言。它只是标记。它无法表达算法。”。或者“正则表达式不是一种语言。它们不能独立存在。它们只是一种语言中的特定语法。“
我也曾经这么认为。但是当我更加仔细地研究之后,我发现这些差异其实很微小。对于语言,在三个主要特征中,我认为第一个是:编程语言本质上是一个交流的媒介,是人类和计算机之间以彼此都可以理解的方式进行交流的符号系统。
“符号”意味着语言包含语法; “可以理解”意味着语言具有附加到语法的含义(更专业的说法是语义)。 该定义涵盖了所有的通用编程语言和所有的DSL。(但是不包括所有的数据流,后面我们再讲)
(顺便说一句,虽然人们通常一起使用“编程”和“语言”这两个词,但是,这里所说的语言,并不仅仅是指人们用它来对计算机进行编程。有时候,计算机也用它来和我们交流,有时候计算机之间也用它来进行交流。从严格意义上讲,我们不应该排除语言的这些应用场景。但是,实际上,我们通常用编程语言做的就是编程。 )
我们再来说一下HTML。它能够告诉计算机,更确切地讲是浏览器,如何来绘制网页。它是人和计算机都可以理解的符号系统(包括尖括号,标签,属性等)(charset属性用来定义编码的字符集,p标签用来定义段落,等等)。
HTML的代码片段如下:
<html>
<head>
<meta charset="UTF-8">
<title>My web pagetitle>
head>
<body>
<p>Hello <strong>worldstrong>p>
body>
<html>
如果你对HTML是一种编程语言的说法有异议。我们可以用Python来输出我们的页面。Python是一种真正意义上的编程语言,你应该没有异议吧?
print ""
print "<html>"
print "<head>"
print "<meta charset=\"UTF-8\">"
print "<title>My web pagetitle>"
print "head>"
print "<body>"
print "<p>Hello <strong>worldstrong>p>"
print "body>"
print "<html>"
显然,这种区别对待HTML和Python的做法是矛盾的。Python化只是增加了复杂性并提供了模板。还有,从控制Web浏览器的角度讲,Python唯一有趣的语义内容就是嵌入在HTML中的内容。出于一致性考虑,我们可以得出结论:HTML虽然比Python更简单且不灵活,但是它确实是一种编程语言。如果你认为Python是一种编程语言而HTML不是,那么,我们应该很容易推断上面这个Python片段是程序,而上面的HTML代码则不是。
嵌入式语言
上面的HTML片段是我们特意设计的。但这种将DSL嵌套在另一种语言中的做法是普遍存在的。以这种方式使用的语言被称为嵌入式语言。它们代表了最常见的面向语言编程。作为一名程序员,即使您不知道LOP的正式名称,您也已经用了很长时间。
例如,正则表达式。我们可能不会认为正则表达式是一种独立的语言。但每个程序员都知道下面这段代码的含义:
^fo+(bar)*$
与HTML一样,我们可以用宿主语言的符号编写等效的字符串匹配计算。例如,Racket支持方案的正则表达式(SRES),它们是使用正则表达式S-表达符号。上面的模式会这样写:此外,如果您将上面的正则表达式输入到您喜欢的编程语言中,这个表达式就会运行。这种行为的一致性是因为正则表达式本身就是一种嵌入式语言,它由POSIX定义。
使用S-expression来定义正则表达式。上面的正则表达式可以表示为:
(seq bos "f" (+ "o") (* (submatch "bar")) eos)
我们还有一个随处可见的嵌入式DSL:数学表达式。每个程序员都知道下面的表达式的含义:但是,Racket程序员很少使用SRE。它们太长而且难以记住。
(1 + 2) * (3 / 4) - 5
你不能把编写HTML称为编程数学表达式本身并不能开发有趣的程序。我们需要将它们与其他语言结构相结合。但与正则表达式一样,这是符合人体工程学和实用的考虑因素。数学表达式有自己的符号和含义,人类和计算机都可以理解,因此它也是一种单独的嵌入式语言。
不能将 HTML 称之为编程
我认为编写HTML就是编程。我认为HTML(还有正则表达式和数学表达式)应该被认为是基本编程语言。这就意味着编写HTML(还有正则表达式或数学表达式)都应该被认为是基本编程。
对于上面的论点你没有必要太在意。如果有谁只知道HTML和算术,就大言不惭地在LinkedIn上将自己标榜为“程序员”的话,大家都会觉得这很过分。这其实是另外的一个话题,即我们通常所说的“程序员”一词在就业市场中所代表的含义。但这不是我们在这里需要关注的问题。
图灵完整性
前面我对编程语言的解释,如果你觉得不太满意,这可能因为你认为一个真正的编程语言必须能够表达所有可能的算法-也就是说,它必须具有图灵完整性。
我理解大家为什么这么想。因为每一种通用编程语言都具有图灵完整性。
问题在于图灵完整性是一个比较低的标准。这只是衡量语言的一个技术指标,但是它并不决定现实世界中的语言应该是什么样的。例如,正则表达式也不具有图灵完整性,但是它却具有较高的使用价值,因为它们用最少的符号表达了具有广泛含义的算法。HTML也不具有图灵完整性,但它却能有效地控制浏览器的展示和行为。与正则表达式和HTML形成明显对比的是bf语言。它虽然具有图灵完整性,但是,即使是最简单的任务也需要大量难以理解的代码。
成为语言的限制条件
到现在为止,我对编程语言的定义还没有包含全部的内容:
二进制格式的数据不符合语言的要求。例如,一个jpeg文件。虽然计算机可以,但是人类却无法理解它。还有PDF文件:如果你打开一个PDF文件,你会发现一些可读的内容。但是这只是PDF的一部分。人们还是没有办法用PDF格式的消息来进行交流。
纯文本文件不是语言。如果有一个包含荷马史诗中伊利亚特的文本文件,我们人类是可以阅读和理解。虽然计算机可以轻松处理文本文件,比如打印其中的内容,但是,文件中的文本内容对计算机来说却是难以理解的。
用户图形界面不是语言。它们是符号系统(由文本和图像组成)。但是它们只能被人类所理解。虽然计算机能绘制用户图形界面,但是却不能理解它们。
语言是接口
在前面的部分,我将编程语言定义为人与计算机之间进行信息交换的媒介。按照这个定义,语言也就顺理成章的可以被称为接口。
这也是我对于语言本身的第二个定义:即面向语言的编程,从本质上讲就是接口设计技术。如果您喜欢习惯接口技术,您也就会喜欢上LOP。如果你不习惯接口技术,你也还可能喜欢LOP,因为它可以帮您实现某些其他方法无法实现的接口。
谈到语言本身是接口的时候,我最喜欢举得例子是brag。它是一种用Racket定义的解析器生成语言。如果您曾经使用过类似lex或 yacc一类的工具,您就知道我们是想根据BNF语法生成一个解析器。举个例子,一个bf语言的BNF语法可以是下面这样:
bf-program : (bf-op | bf-loop)*
bf-op : ">" | "<" | "+" | "-" | "." | ","
bf-loop : "[" (bf-op | bf-loop)* "]"
有了brag,我们就不需要这样繁琐。在创建解析器的时候,我们只需像下面这样将#lang brag语句添加到文件中,BNF语法就会被神奇地转换为bra代码:要使用通用语言创建解析器,我们必须将这个语法转换为一系列本机代码。
#lang brag
bf-program : (bf-op | bf-loop)*
bf-op : ">" | "<" | "+" | "-" | "." | ","
bf-loop : "[" (bf-op | bf-loop)* "]"
即使有解析器的生成器,这本身也是一项繁琐却没有意义的工作。我们已经注明了语法,为什么还要这样做呢?
这是我最喜欢举的一个LOP的例子,因为它比其它冗长的做法好很多。如果使用通用语言,这种接口基本上是不可能实现。就是这么简单!编译之后,该文件将生成一个名为parse的函数,该函数就可以实现对应的BNF语法。
但是面向语言的程序员都会这样做。
语言作为接口的优势
这让我想到了我的第三个也是最后一个关于语言的定义,即在所有的接口中,语言具有它独特的优势。您别嫌烦,我发现LOP在下面这些情况下很有优势:
1. 当你想创建一个可供不太熟练的程序员、非程序员或懒惰的程序员们使用的接口的时候。多说一句,您千万不要低估懒惰的程序员数量。
例如,Racket有一个精心设计的Web应用程序库。但是您也可以使用web-server / insta快速启动一个简单的Web服务器:
#lang web-server/insta
(define (start request)
(response/xexpr
'(html (body "Hello LOP World"))))
Matthew Flatt在他的文章“在Racket中创建语言”中演示了一种可生成可播放文本的语言。与brag一样,它看起来更像是一个规范而不是一个程序,但是它确实能够运行:
#lang txtadv
===VERBS===
north, n
"go north"
south, s
"go south"
get _, grab _, take _
"get"
===THINGS===
---cactus---
get
"Ouch!"
===PLACES===
---desert---
"You're in a desert. There is nothing for miles around."
[cactus, key]
north
meadow
south
desert
2. 当你想更简洁地表示的时候。正则表达式是一个例子。另外一个例子是我的DSLPollen,一种用于制作在线书籍的语言(包括这一本)。Pollen很像Racket。它们明显的差别是对于Pollen,你需要以文本模式开始,并使用一个特殊字符来表示Racket命令,这些Racket命令被执行并且将执行结果添加到文本内容中。本段的第一部分就是这样编写的:
#lang pollen
When you want to simplify the notation. Regular expressions are one example.
My DSL ◊link["https://pollenpub.com"]{Pollen} is a language for making online books.
desert
Pollen会插入必须的标签并将这些内容转换为正常的HTML。使用Pollen,我可以像手工编写HTML那样完全地控制页面的内容,同时,又避免了手工操作可能带来的错误,比如,不小心省略了结束标记。
简化表示的另一个例子是lindenmayer,它是一种用于生成和绘制图形的语言,称为Lindenmayer系统,它可以绘制下面的图像:
如果使用普通的Racket,Lindenmayer程序是这样写的:
#lang racket/base
(require lindenmayer/simple/compile)
(define (finish val) (newline))
(define (A value) (display 'A))
(define (B value) (display 'B))
(lindenmayer-system
(void)
finish
3
(A)
(A -> A B)
(B -> A))
只需要更改文件顶部的#lang指令,您就可以将它进行简化成:
#lang lindenmayer/simple
## axiom ##
A
## rules ##
A -> AB
B -> A
## variables ##
n=3
该语言假设您已经了解了Lindenmayer系统。这种简化的表示方法可以很容易地实现您的要求。
3.当你想基于已有的表示法构建新的系统时。前面我们讲到的brag,就是一个使用BNF语法作为源代码的DSL。
#lang brag
bf-program : (bf-op | bf-loop)*
bf-op : ">" | "<" | "+" | "-" | "." | ","
bf-loop : "[" (bf-op | bf-loop)* "]"
还有另一个例子,如果尝试过Pollen的人说“它确实很酷,但我还是更喜欢Markdown。”。这也能够做到。pollen /markdown是Pollen的一个变种,它支持Pollen的语义,同时又能接受普通的Markdown符号:
#lang pollen/markdown
When you want to simplify the notation. Regular expressions are one example.
My DSL [Pollen]("https://pollenpub.com") is a language for making online books .
通过将Markdown解析器与现有代码相结合,我只花了大约一个小时来创建这个变化的形式。你觉得快不快?
4. 当你想为其他语言创建中间格式的时候。JSON、YAML、S-expressions和XML都是定义数据格式的DSL。对于这些数据格式,对于机器来说,既可以是它们的输入,也可以是他们的输出。
在Beautiful Racket一文中,我提供了一种称为jsonic的教学语言。它允许我们在JSON中嵌入Racket表达式,从而使我们也可以用JSON进行编程。比如:
#lang jsonic
// a line comment
[
@$ 'null $@,
@$ (* 6 7) $@,
@$ (= 2 (+ 1 1)) $@,
@$ (list "array" "of" "strings") $@,
@$ (hash 'key-1 'null
'key-2 (even? 3)
'key-3 (hash 'subkey 21)) $@
]
它可以编译出下面的JSON内容:
[
null,
42,
true,
["array","of","strings"],
{"key-1":null,"key-3":{"subkey":21},"key-2":false}
]
5. 当程序的大部分是配置信息的时候。例如,Dotfiles就可以被认为是DSL。另外一个更复杂的例子是由Jesse Alama开发的Riposte,它用于测试JSON格式的HTTP API:
#lang riposte
$productId := 41966
$qty := 5
$campaignId := 1
$payload := {
"product_id": $productId,
"campaign_id": $campaignId,
"qty": $qty
}
POST $payload cart/{uuid}/items responds with 200
$itemId := /items/0/cart_item_id
GET cart responds with 200
作为一种微型脚本语言,Riposte比普通的dotfile更智能。它隐藏了HTTP事务所需的所有中间代码,并让用户专心编写测试脚本。它可以使您专注于你关心的内容。
为什么选择Racket?
人们对于LOP的一个常见批评是“为什么还要定义特定领域的语言?这难道不比开发特定的程序库需要更多的工作吗?“
如果你有合适的工具,那确实不会需要更多的工作。Racket就很不一样:它的设计初衷就是为了支持LOP。因此,在Racket中实现DSL比其他方法更快、成本更低,同时也更容易。例如,在本书的第一篇教程中,我就向大家展示了即使您从未使用过Racket,您也可以在一小时内定义一门语言。
从本质上讲,在Racket中每个DSL实际上都是一个源文件到源文件的编译器。它将DSL的符号和语义转换为对应的Racket程序。也正因为如此,Racket DSL的运行速度不会像用C语言编写的程序那样快。但它也使您可以在每个Racket DSL中访问所有Racket的工具和库。所以您在程序执行性能方面的损失,可以通过使用的便利来弥补。正是因为DSL方便使用,同时使用的成本又低,所以它们应用的范围越来越广。
总而言之,使用DSL不一定比开发特定的程序库需要更多的工作。而且,正如我们已经看到的,作为一个接口,一种语言可以完成特定的程序库无法完成的任务。
为什么选择宏?
由于Racket DSL会被编译为Racket,使用Racket的程序员需要编写一些语法转换器,将DSL符号转换为Racket。这些语法转换器被称为宏。实际上,宏可以被认为是Racket编译器的扩展。
Racket的宏很庞大、优雅,也是Racket的精华。在本书中,我所重点讲的都是Racket宏让人觉得有趣的地方。比较突出的包括下面两点:
Racket有一个称为语法对象的数据结构,它是宏之间进行交换的媒介。普通的字符串只能包含原始代码,一个Racket语法对象可以保留它的层次结构,还可以包括像词法上下文和源位置等的元数据和被称为语法特性的任意字段。这些元数据在各种转换期间和代码关联。(详细信息,请参阅语法对象。)
Racket宏是健壮的,这意味着缺省情况下,宏生成的代码保留了定义宏的词汇上下文。实际上,这减少了为使DSL正常所需的大量辅助性工作。(有关详细信息,请参阅健壮。)
是否有可能在Python中实现DSL?当然可以。实际上,我就是用Python写了我的第一个DSL,我现在仍然用这个DSL来实现我的类型设计工作。尝试一次就够了。从那时起,我就一直使用Racket。
结论:充分利用LOP
我猜到现在,您可能会想到:
“LOP似乎很有趣,但我不知道我会用它做什么。”。当然,我也没指望您马上加入LOP的队伍中。相反,我的目标是以有效的方式改变您你的思维。您现在已经了解了以前不熟悉的工具。今天,您可能想不到怎么去应用它。但总有一天,你会遇到一个用现有语言所不能解决的问题。那时候,LOP就将派上用场。
“好吧,你已经说服了我,但我还是无法将LOP或者Racket在我的工作场所里使用。”。Jesse Alama在Jesse Alama’s story一文中,讲述的他是怎样让他的同事们接受他的DSLRiposte。(摘录如下,请注意我加粗的部分):
如果你想在用Racke做事之前得到许可,我觉得这不太可行。我认为你最好先做些事情,然后再解释它们的好处,这样会更容易。您可以与同事讨论他们的工作并询问他们:“我们如何模拟API的变化并确定我们做对了?“。对于这个问题的答案是:”写一个Riposte脚本。“那时我很清楚我所制作的[DSL]会带来真正的好处。我甚至没有刻意地“推”Racket。我只是介绍DSL并向他们展示DSL可以怎么帮助他们。
在我曾写的“为什么选择Racket?为何选择Lisp?”一文的结尾,我说Lisp语言“让你有机会发现自己作为程序员和思想家的潜力,从而提高你对自己能够取得的成就的期望。”
LOP提供了类似的机会:它可以提高我们对编程语言能够做什么的期望。语言不是黑盒子。它们是我们可以设计的接口。这样,我们用程序可以做更多的事情。
如果你能找到更好的编程技术,建议你使用。现在我有了LOP&Racket,就不会再回到过去。
更多阅读资料
我和Matthew Butterick编写的Beautiful Racket(https://beautifulracket.com/)。你当前正在看的在线图书。但是你现在是在本书的附录部分。本书的主要部分是一系列渐进式LOP教程,全部依赖于Racket及其出色的宏系统。
Racket学 School 2019(https://school.racket-lang.org/)。今年夏天,Racket学校提供了两个LOP课程:由我教授的为期三天的美丽的Racket工作室和为期五天、由Racket设计师执教的如何设计语言。
由Jesse Alama 编写的Language Oriented Programming in Racket: A Cultural Anthropology(https://gumroad.com/l/lop-in-racket-cultural-anthro)。这是一个很吸引人的标题,里面是Jesse与众多Racket爱好者(包括我在内)进行的一系列访谈,其中包含了对如何将LOP融入我们的工作的截然不同的观点。
Matthew Flatt所编写的Creating Languages in Racket。(http://queue.acm.org/detail.cfm?id=2068896)Matthew Flatt是Racket的首席架构师(并且写了这本书的前言)。这篇简短的文章通过创建游戏的DSL来向大家展示了怎么样不断提高DSL设计水平。
实现的Racket 的例子:More examples(https://beautifulracket.com/appendix/domain-specific-languages.html#racket-implemented-dsls)和even more(http://docs.racket-lang.org/search/index.html?q=H%3A)。
原文:https://beautifulracket.com/appendix/why-lop-why-racket.html
本文为 CSDN 翻译,转载请注明来源出处。
Python 的这几个技巧,简直屌爆了
https://edu.csdn.net/topic/python115?utm_source=csdn_bw
【END】
作为码一代,想教码二代却无从下手:
听说少儿编程很火,可它有哪些好处呢?
孩子多大开始学习比较好呢?又该如何学习呢?
最新的编程教育政策又有哪些呢?
下面给大家介绍CSDN新成员:极客宝宝(ID:geek_baby)
戳他了解更多↓↓↓
热 文 推 荐
开了个会:破局企业云通信,华为加速 Buff 开发者!
☞ 甲骨文中国确认裁员 900 余人;网易回应邮箱账号遭公开叫卖;我国网民达 8.29 亿 | 极客头条
☞ 漫画:如何给女朋友解释灭霸的响指并不是真随机"消灭"半数宇宙人口的?
☞她高中发明著名算法,保送清华姚班,斯坦福 AI 实验室负责人高徒 | 人物志
真正勇猛的程序员,敢于让鲁迅崩溃!
从Pixel 3a到Android Q,一份谷歌AI能力的“成绩单” | Google I/O全程回顾
没有一家公司可以逃避边缘计算 | 技术头条
☞20年无人能破的RSA算法发明人出的密码学难题, 竟被这个无名程序员3年破解!
☞她说:为啥程序员都特想要机械键盘?这答案我服!
点击阅读原文,输入关键词,即可搜索您想要的 CSDN 文章。
你点的每个“在看”,我都认真当成了喜欢