python 有一个很-O 选项我一直很好奇这个选项是干嘛的, help 写的是:
-O : optimize generated bytecode slightly; also PYTHONOPTIMIZE=x
-OO : remove doc-strings in addition to the -O optimizations
python 优化选项可以产生更小的 bytecode 文件,我试着选了一下,确实小了一点,但是对性能提升并没有什么用,后来看官方邮件是这么回复的:
> Py_NoSiteFlag = 1...assuming you don't need to load site.py
>
> for example:</br>
>
> extern int Py_OptimizeFlag;
> extern int Py_NoSiteFlag;
> ...
> if( !Py_IsInitialized() ) {
> Py_OptimizeFlag = 2;
> Py_NoSiteFlag = 1;
> Py_Initialize();
103
liangmishi 2016-08-03 13:31:26 +08:00
@SlipStupig 当然不能。
之所以会提那个问题,是想知道当前请求的数据库查询阻塞了,是不是其他请求的查询也会跟着阻塞,进而影响请求的响应时间。如果是整个数据库挂掉引起的阻塞,那就跟他们讨论的点一点关系都没有了。。从 @GeekGao 的建议可知, kill 掉慢查询,或者使用非阻塞的库即可。具体问题具体分析吧。 |
104
serial 2016-08-03 13:42:41 +08:00 1
@dzhou121
我现在让你写个 web servie ,进行日志分析任务。要求提供分布式存储、观看任务状态的消息队列、实时客户端回馈。你给我用 python 设计设计来。 请问,我该用哪些 python 库可以实现。我的要求高了,我这是一个认真的项目,你给我的库必须是工程级别,经过认真测试应用的,而不是某个菜鸟凭着兴趣爱好摸索的一些实验品。我要求满足:至少每秒处理五千次读请求数、每秒处理一千次写请求数。(这个要求不高吧,顶多算分布式环境入门级别) ========================================================== 看看当今流行语言的 Benchmark : https://github.com/kostya/benchmarks Python 就是个垫底的货,写写单机的东西就行了,干干运维就行了,千万别把自己写的 python 程序当回事。 |
105
hanfeng3015 2016-08-03 13:58:50 +08:00 1
谈语言的“速度”,其实是一句空话。语言只负责描述一个程序,而程序运行的速度,其实绝大部分不取决于语言。它主要取决于 1)算法和 2)编译器或解释器的质量。编译器和语言基本是两码事。同一个语言可以有很多不同的编译器实现,每个编译器生成的代码质量都可能不同,所以你没法说“ A 语言比 B 语言快”。你只能说“ A 语言的 X 编译器生成的代码,比 B 语言的 Y 编译器生成的代码高效”。这几乎等于什么也没说,因为 B 语言可能会有别的编译器,使得它生成更快的代码。
举个例子吧。在历史上, Lisp 语言享有“龟速”的美名。有人说“ Lisp 程序员知道每个东西的值,却不知道任何事情的代价”,讲的就是这个事情。但这已经是很久远的事情了,现代的 Lisp 系统能编译出非常高效的代码。比如商业的 Chez Scheme 编译器,能在 5 秒钟之内编译它自己,编译生成的目标代码非常高效。它可以直接把 Scheme 程序编译到多种处理器的机器指令,而不通过任何第三方软件。它内部的一些算法,其实比开源的 LLVM 之类的先进很多。 |
107
m8syYID5eaas8hF7 2016-08-03 14:12:54 +08:00
@serial 你说的没错。。。我并没有感觉到你在黑 Python 啊,只是说出了 Python 的劣势而已,下面很多人莫名高潮让我非常诧异。。。
|
108
serial 2016-08-03 14:38:06 +08:00 1
@hanfeng3015
错误,听说过 Lisp 机吗? Lisp 机败给了 C 机器,原因就是速度。 我拿 JavaScript 和 C 来比较。以 64 位 CPU 和 64 位操作系统来讲,在 JavaScript 中所有整数、浮点数都是按照 64 位存储的。也就是说,你存储一个数组 [1, 2, 3],至少占用 64 * 3 位。在 C 中不是,整数有 8 位、 16 位、 32 位、 64 位,你存储一个数组 [1int8, 2int8, 3int8] 只占用 8 * 3 位,存储 [1int32, 2int32, 3int32] 只占用 32 * 3 位。 懂我的意思吗?当你计算的时候, JavaScript 需要 CPU 扫描 64 * 3 个比特,而 C 呢? 其他更多了,比如数组。 JavaScript 数组是可变长度的。什么意思? 你存储 a = [1, 2],然后 a.push(3)。发生了什么? JavaScript 的解释器先在堆上分配一块大内存(尽可能多,绝对不是 64 * 2 ),然后把 3 放进去。这块内存填满怎么办? 按照 2 的倍数分配一块新内存,把数据复制过来,把 3 放进去,然后释放旧的内存。 你做了 2 次分配, 1 次释放,而且占用内存还特别大。 C 呢?你存储 a = [1, 2], a 就不能再变了,它始终分配 32 * 2 个内存。想要堆?使用 malloc() 分配指定的单元内存,然而,它的长度是固定的!这就是效率的秘密。 性能和编写方便,是完全对立的。 |
109
serial 2016-08-03 14:40:26 +08:00
|
110
loveshouhu 2016-08-03 15:17:02 +08:00
看大神们互掐,掐出了好多不会或者不熟悉甚至不知道的知识点。楼主我们一起努力学习吧。说不定有一天可以像他们一样谈笑风生。😀😀😀
|
111
dzhou121 2016-08-03 15:19:51 +08:00
@serial
我从没有说 Python 性能好这也能用那也能用,你这个场景让我用 Python 实现的意义是什么? Python 的性能是很差,所以需要性能的地方我当然不会用 Python ,但是所有系统的最重要的需求是性能吗?不见得吧。就拿 Openstack 来说, Nova 不需要特别强的性能呀,因为就是提供管理 VM 的 API 。但是 Ceilometer 就不行,需要很强的性能, Python 其实不合适的。 >>Python 就是个垫底的货,写写单机的东西就行了,干干运维就行了,千万别把自己写的 python 程序当回事 不是说一个程序一定要 scale 到成千上万的机器才是有意义的,程序本身没有意义,程序能解决的问题本身才有意义 |
112
yszx 2016-08-03 15:20:35 +08:00
语言大战么····
那我说一个没多少人知道的····python 的循环-sleep 有 bug ,有一定几率 sleep 了之后就再也起不来了。曾经困扰过我们很久 |
113
GeekGao 2016-08-03 15:44:36 +08:00
就 @serial
关于数据处理,这眼界也真是 [狭窄] 得令人无语,你做不到不代表别人做不到啊, 国外就不说了,公开资料来看国内的豆瓣就用 Python 重写 Spark 处理数据啊 https://www.douban.com/subject/10774736/ 你自己慢慢看吧。如果非要抬性能的杠也没啥意思,你用啥觉得性能好就用啥呗,俗话说 u can u up 嘛。 在 Sun 没有收购 HotSpot 技术前, Java 性能也被人诟病,又如何,即便现在 C/C++程序员可能还对 Java 有偏见, 但并不影响开源社区用它去做分布式和大数据应用! 还有,代码写不好不要怪语言和编译器,多反思自己用的技术是不是很契合自己的应用场景,换句话说你用 C 撸了一堆所谓 [高效] 的代码,速度在优于 Python ,实现的玩意不值钱,有个逑用。 |
114
GeekGao 2016-08-03 16:09:37 +08:00
@dzhou121 我觉得也没必要讲那么多了,有的人可能就是一根筋,要么是真的傻,要么就是故意吐槽来的。浪费时间讲太多没用的。
|
115
crossmaya 2016-08-03 17:06:26 +08:00
肿莫了。
|
116
xi2008wang 2016-08-03 17:45:03 +08:00
大家可以看看这个链接: python 解释器实现 https://www.zhihu.com/question/25307289
我最近也看了点 python 源代码,一句话就是: python 好用、灵活 但是实现复杂 结果就慢了 所以要高效就用 C 、 python 混合编程 serial 同学说的确实没说错,只不要推广你的 Nim 语言这软广那是比较明显 😄 |
117
DoctorCat 2016-08-04 01:32:19 +08:00
“ IO 阻塞与否跟编程语言没多大关系的, OS 、各类服务 driver 不支持的话,众多编程语言一起哭吧 ”
这话没错啊,如果操作系统不对 IO 异步做支持,也没办法啊,我想你是没经历过 PC-DOS 、小霸王学习机的年代吧,还有啊倘若我照着《 30 天自制操作系统》些个 OS 雏形,也可以不支持线程和 fork 额,这样你什么样子的编程语言也都无可奈何了吧 |
118
pyufftj 2016-08-04 06:45:16 +08:00
从来没有对 python 进行过优化,因为现在计算机的 cpu 不是瓶颈,数据库读写和网络传输才是瓶颈。
|
119
serial 2016-08-04 08:34:58 +08:00
|
120
xiaohuangya 2016-08-04 12:14:02 +08:00
初学 python 。看到各位大牛在斗嘴。我习惯性的留个名字。(ง •̀_•́)ง
|
121
wizardforcel 2016-08-04 19:30:51 +08:00
@serial 你以为程序员就写应用??真是坐井观天,整天写应用也不觉得烦。
我就告诉你,在数据(或者数据分析)领域,多数产品都不是软件成品而是报表,数据的分析结果。代码都是一次性的,这种情况下,你拿 C/C++/Java 去写一次性程序,每次都要建个项目你蛋不蛋疼??性能差又怎么样,又不是给用户用,你就差那么点时间,急着去投胎嘛?? |
122
wizardforcel 2016-08-04 19:37:47 +08:00
@serial
[错误,听说过 Lisp 机吗? Lisp 机败给了 C 机器,原因就是速度。 ] 那是以前,现在计算资源过剩,性能差又如何呢?? 别整天瞎 jb 谈性能瓶颈,别以为你那边整天需要高性能,就以为全球人民都需要高性能。你可以觉得你在高性能方面做的不错,但是大部分情况中你讨论的前提就不成立。你这就是把自己的需求当成所有人的需求。 多数情况并不是那么苛刻,需要的是开发效率,你还懂产品??等你拿 C 语言写出来了,别人拿 Python 写的早就上市了。 软件开发就是要讲权衡,过度追求运行效率就是不对。 Lisp 机单纯是因为速度??易读性差和生态不丰富也得背锅吧??真是无知。 以上 |
123
wizardforcel 2016-08-04 19:52:30 +08:00
|
124
upczww 2016-08-05 12:33:06 +08:00
火药味很足,菜鸟只能看楼上谈笑风生,希望有一天也可以
|
125
FrankHB 2016-08-06 01:01:47 +08:00
@hanfeng3015 王垠的私货要引用也行,但别不到点上。顺便他是一向很鄙视 Python 的。
语言的“速度”,其实是一句空话——这倒是没错,不过原因是——“速度”是个向量。搭配不当的语病 fail fast 就行了。 “语言只负责描述一个程序,而程序运行的速度,其实绝大部分不取决于语言。它主要取决于 1)算法和 2)编译器或解释器的质量。” 这里的外行在于语言忽略了语言钦定的语义。把一个语言设计成编译器或解释器实现得无论如何不得不低效(否则就不算符合语言规范要求)是很容易的。 不过 Python 的“慢”主要并不是这个问题。例如, GIL 就不是 Python 的锅而是 CPython 的锅。 然而最主流的实现如此熟练地甩锅给用户且用户经常不得不接受,也从侧面体现出设计之外的辣鸡了。 另外一个情况是语言自身描述性能规格的时候,比如要求时序。这里就没必要展开了。 所谓“ Lisp ……代价”指的更像是 Lisp 自身的抽象的缺陷。不过王垠近年来似乎终于知道 Lisp machine 一条路走到黑到处都不能 O(1)了? |
126
FrankHB 2016-08-06 01:16:07 +08:00
@pyufftj “你的”计算机,谢谢。
@wizardforcel 我以最终用户的身份鄙视你的观点。 别以为你那边不需要高性能,就以为全球人民都不需要高性能。事实是能论证清楚哪里不需要“高性能”的用户压根就没几个,更别提逗比开发者乱加中间层挥霍的能有多少了。或许你都没感觉到能耗也算是性能需求的一部分吧? 至于“多数情况”,根本哪壶不开提哪壶。服务器吞吐量厨惯了也就罢了,随便一个客户端应用还 yy 整个机器内存“不用白不用”,谁 tm 给你的胆把用户的宿主环境不当多任务系统看? 这种不经过大脑分析需求“性能过剩”论给我的计算环境添堵有很大的功劳。比如说,市面上常规手持设备就没有一个能面向最终用户指定任务保证响应的,谁的锅? 面对横竖都是被坑的局面,用户能怎么办?把生态推倒全部自己撸一遍?开玩喜呢。 市场手段效果不显著,那么除了对付 dssq 见一次打死一次以外就没有效的通用解法了。 |
127
wizardforcel 2016-08-06 08:26:51 +08:00 via Android
@FrankHB
常规手持设备的“故意连锁唤醒和故意驻留内存”和我说的“为了开发效率牺牲一些运行效率”是两回事,不要偷换概念,因为你知道我说的是哪一种。 啥叫“服务器厨惯了”??我不否认服务器需要考虑性能,但普遍做法不是把应用往死里整,而是负载均衡,即把应用做成可扩展的,之后垒机器就行了。这个做法跟 LZ 是背道而驰的。 LZ 的做法唯一有用武之地的情况就是实时系统--需要快速响应的系统,可这种一般用于科考或救援。这种算不上多数情况就不用我说了吧 。在搞互联网的圈子里说 python 不适合这种环境,这是在逗我?? 另外, LZ 使用 lisp 不火 python 火的论据,是否也说明性能在以前重要,现在不是那么重要了??(手动滑稽) |
128
FrankHB 2016-08-06 19:39:23 +08:00
@wizardforcel 我看到你要提的合理性就是工程上需要 trade off 以及性能需求不总是第一位的,这点很简单,不用那么漏洞百出的论调来复杂化和削弱可信性。
我旗帜鲜明地反对的是不分析性能需求的外延就主张放弃性能。只要能保证按照约定的质量按时交付,对性能吹毛求疵好歹对最终用户没有损害;而不仔细分析拿性能当妥协,很难保证不损害用户(本来理当享有)的利益。一家两家这样做没什么问题,但当这样成为业界主流时,不兼职开发的用户就只能挨宰了。这显然不是应该提倡的情形。 注意分析清楚保证没有 false negative 的性能缺陷实际上几乎总是很困难的(换句话说,即便是约定清楚了指标,你也不太可能让性能恰好降低到符合要求而腾出资源让位给其它需求),所以优先强调性能是一种保守(尽管可能低效以及同样反智)的工程策略。 这种主张有一些庸俗版本的说法,其中有不止一个问题。比如说,“计算资源过剩”——同时忽略了背景以及不同计算资源基本上很难全面过剩(真全过剩了用户算是一定意义上活该)的事实。 “服务器”的问题你刚好理解反了。我的意思是服务器(准确地说也不是所有服务器)常常恰好不关心某一种“性能”,所以写惯了服务器应用的开发者往往在这里少根筋。 具体说来,像 Web 服务器应用,同样是性能需求,吞吐量通常远远重要于交互操作的响应。这使得使用 STW 的 GC 作为一种优化是可以被接受的。但是有些人基于某些理由不顾性能需求的差异,就 yy 这样的场景同样适合于其它领域,把选型策略无条件上升到方法学;甚至不惜直接代替用户决策来保证系统可用性。 当然,大部分客户端的响应其实也并不是非常高,分时系统是可以接受的。但是实际用起来如何呢?当市面上主流产品没法保证不堆硬件就不损害日常体验,是不是反省是谁该给原则错误买单了? 嗯,上面婊的是 Java ;用户熟悉的代表坑货是 Android+OOM killer 。 不过考虑到语言内建 GC 的流行,大部分没有考虑清楚适用范围的语言在这里都难逃其咎。和这个主题直接相关的是, Python 从原则上(想做通用领域语言,想“简单”)就很不擅长应对这点,加上实现的实际表现也不咋地,所以成为靶子也不奇怪。 注意,这里说的是 Python ,不是“互联网的” Python 。 Python 从来就不是互联网行业专用的 DSL 。否则,一开始就没必要讨论了。 Lisp 当然有另外的槽点,不过和这里的话题比较无关就是了(至少 Lisp 不存在一种大规模的主流实现能拉到那么多仇恨)。 |
131
serial 2016-08-08 08:33:27 +08:00
|
132
serial 2016-08-08 08:39:17 +08:00
@wizardforcel
恰恰相反, Lisp 的价值就是可读。 Lisp 的语法就是语法树,(* (+ (- 1 2) 3) 4) 。这种语法对机器非常有利,机器可以更容易的生成“人工智能”代码。但是,根据摩尔定律,硬件的性能提升每年才一倍,要达到这种分析和生成,还要等到猴年马月。 |
133
alexapollo 2016-08-08 11:07:58 +08:00
@serial
> 我现在让你写个 web servie ,进行日志分析任务。要求提供分布式存储、观看任务状态的消息队列、实时客户端回馈。你给我用 python 设计设计来。 > > 请问,我该用哪些 python 库可以实现。我的要求高了,我这是一个认真的项目,你给我的库必须是工程级别,经过认真测试应用的,而不是某个菜鸟凭着兴趣爱好摸索的一些实验品。我要求满足:至少每秒处理五千次读请求数、每秒处理一千次写请求数。(这个要求不高吧,顶多算分布式环境入门级别) 这些很简单的,而且业界都有开源的工业化组件了,有这些问题说明没有接触过工业化的 Python 编程吧。。 |
134
greenlet 2016-08-08 12:53:54 +08:00
@serial 你贴的测试没有什么意义, Python 在 CPU 密集型应用上的性能比 C/C++要慢几十倍这是常识。你的问题在于:
1.不会有人傻到用 Python 去写 CPU-intensive 应用(除非那个人只会 Python )。 2.多数 web 场景下 CPython 解释器的性能不是明显瓶颈,有大量的时间耗在 I/O 上(这个做过 profile 不可能不知道),要不然为什么 Erlang 在个别服务器端领域莫名其妙火起来了?尤其在 Gevent 、 Celery 这类东西出来之后,高并发场景下 Python 已经被大大续命了。我司(某二线互联网)的自定义错误页跳转是我组用 Python 写的, C30k 以内客户端都能有满意的响应时间。个别小功能依赖 CPU 时我们的方法是用 C 写这个模块。 接触 C50k 、 C100k 级别项目的人是少数,我接触过极个别因 CPython 性能太烂而不得不换语言的场景都是跟启动线程的速度太慢有关。 |
136
GeekGao 2016-08-08 18:19:30 +08:00
这种技术贴下还有水军啊: http://www.v2ex.com/member/loohawe/replies
V2 站的某些人真是有趣! |
137
wizardforcel 2016-08-08 22:13:21 +08:00 via Android
@FrankHB
你这句话反过来理解就是“你反对不分析性能需求的外延就主张某种语言性能差”。再一次打了 LZ 的脸。 服务器的例子中,由于 python 虽然计方面性能差, io 还说的过去,所以他们并不关心计算性能。但是,吞吐量算不算性能的一部分?? LZ 拿个跑分,也就是只能反应计算性能的东西说 python 性能差,但为什么它在 io 密集的应用中运行得好好的??在逗我?? 最后, python 声称是通用语言,所以你就认为它在每个领域都要工作得很好??根本就没有通用的语言,即使是那些自己声称的。 python 在互联网中够用,数据科学中也够用,不够用的地方换其他语言就得了。 |
138
wizardforcel 2016-08-08 22:39:27 +08:00 via Android
|
139
wizardforcel 2016-08-09 17:40:12 +08:00
@serial
[恰恰相反, Lisp 的价值就是可读。 Lisp 的语法就是语法树,(* (+ (- 1 2) 3) 4) 。这种语法对机器非常有利,机器可以更容易的生成“人工智能”代码。] 函数调用表达式和前缀表达式具有相同的优势,比如 mul(add(sub(1, 2), 3), 4) ,它同样也对机器有利。而且函数在几乎所有编程语言里都是这个写法。 |
141
serial 2016-08-10 18:02:34 +08:00
@wizardforcel
请问你学过编译原理吗? mul(add(sub(1, 2), 3), 4) 对机器有利? 有相同的优势? 麻烦你好好读读《 Compilers: Principles, Techniques, and Tools 》先搞清楚怎么抽象和解析语法树, OK ? mul(add(sub(1, 2), 3), 4) 对机器有利? 你在讲相声呢。 |
143
appleorchard2000 2016-08-11 12:02:33 +08:00 1
@serial
twisted, tornado, asyncio, eventlet, gevent 这些主要就只有两种设计风格,各自风格内部是可以兼容的。比如 twisted 与 tornado 官方有为彼此兼容推出模块( http://www.tornadoweb.org/en/stable/twisted.html), eventlet 与 gevent 都是基于 greenlet ,甚至连 API 都是基本雷同。 monkey patch 是 eventlet 与 gevent 背后的惯用手法(当然你也可以不用 monkey patch, 而是对应得采用他们所提供的 io 库的非阻塞版本,标准库中的所有 io 模块对应的都有实现,包括 os, socket, queue 等等。) monkey patch 手法的手坏,我觉得仁者见仁智者见智,对我个人来说他似乎看起来是有些 evil ,但也确实在实际的使用场景中大有用武之地。 对于 asyncio 来说,与之搭配的异步 io 库基本都在这里: http://asyncio.org/。 以上几个框架,背后的 io 多路复用技术都是基于 select, poll, epool , kqueue ,实现了自己的 hub 或者叫 ioloop 。 纵上, twisted/tornado/asyncio 的 generator style 异步与 eventlet/gevent 的协程异步都可以完成自己的任务。相应的,各自阵营也都有自己完备的异步 io 解决方案,我看这里没有你所诟病的问题。至少, 基于 python 的技术栈是能够解决异步与阻塞的问题的,也是能够实现高并发的。 当然,要想在上述几个阵营之间切换就不现实了,还有 python2 -> python3 之间的升级也是问题。这是事实。 此外, 对于你所提到的几个事实,我有以下疑问: 说 openstack 自己实现了所有的异步驱动,这是啥意思, openstack 中的异步都是基于 eventlet 的 monkey_patch ,将标准库中的所有 io 行为替换为异步版本,据我所知这里不需要自行实现任何的所谓“异步驱动”。 “放弃高并发使用 django, flask ”这个说得也是奇怪, django 只是使用 python 来制作 wsgi 程序,而 wsgi 程序可以交给 apache/nginx 等来调用,此时的高不高并发,完全是由 apache/nginx 来支持的,跟 django 有什么关系。 另外 @GeekGao 说的 “ io 阻塞不阻塞跟语言没有关系”, 我不知道你是怎么理解的,使用 python 这种语言是即可以写出阻塞式的 io 程序,也可以写出非阻塞式的 io 程序,这只取决于你在调用系统调用的时候,是否指定了相应的参数而已。诚然 python 标准库中默认都是使用阻塞式的 io ,也许你诟病的就是这个? 最后,明确一下我个人的看法, python 完全是可以使用异步与高并发的, io 不是其短板, cpu 确实是(cpython)。时至今日, openstack, douban, zhihu 这些巨大软件体都基于 python 来实现的时候,我认为像 “ python 是胶水语言,是像 shell 一样的脚本语言”这样的论断,是不攻自破的。然而,这并不是我说我很喜欢 python ,与 go 语言的异步相比, 我也认为 python 真的有够折腾,有够 ugly ,而且其 GIL 导致它只能使用 ”进程 + 协程“ 的组合,而不能使用 "线程 + 协程“ 的组合,等等。 python 确实让我感觉不爽,不过没有在座某些人说的那么不堪。 |
144
appleorchard2000 2016-08-11 12:09:39 +08:00
@serial 至于你提到的数据库连接库问题,忘记说。如果是基于标准库中的 socket 来实现的,基于 eventlet/gevent 要实现异步很容易,直接 monkey_patch 就可以。 反而是 MySQLdb 这种基于 C 扩展实现的模块,才有问题。这种没法使用 monkey_patch 来解决,不过 eventlet/gevent 为这种情况提供了线程包装,可以将数据库操作扔到线程之中执行,以弥补这个缺陷。
|
145
brucedone 2016-08-11 13:32:04 +08:00
》翻页吧 python 慢 这个是事实,为了运维方便 从把 shell 迁到了 python ,之前分钟级别的处理变成了小时级
@YORYOR 我不知道你说的慢是哪里慢,如果仅仅是因为换了语言就说 python 导致的慢,就好比你之前骑车上班,而现在坐在车里一边大骂怎么还不开车,怎么这么慢一样。 |
146
FrankHB 2016-08-11 14:36:09 +08:00
@wizardforcel 你还是没跳出套。
假设只侧重于某种性能,不依赖于另一种性能,让性能在整体上看起来不成问题,这没啥大不了的。(或者说,算是分析过了一些需求,应当赞赏?不过我觉得这是份内义务劳动……) 但一个怎么看实际上都已经通用过头的语言还用这种手段掩饰设计问题就流氓了点。 通用语言当然不是指面面俱到。一般来说通用语言需要满足不特定领域专业用户的期望,其中之一体现在工作得明显不够好的领域自觉退让,而不是赖着“能用”留下一坨不成熟可靠的解决方案,增加该领域用户的本不必要的选型成本。 技术上来讲通用和不通用没有很明确的界限,更多体现在设计者的表现的意图以及实际上扩散到了多少应用领域上并且流行了。对比一下, SQL 能干的活也挺多的,但还算是比较老实当 DSL 而不是到处刷存在感,所以姑且没有那么大问题。 当然 Python 某种意义上表现突出也不算一定就烂(其实光看技术方面的问题并没有某些 SQL 方言严重),可以用有足够相似点的实例对比来偷懒地洗。 比如你可以拿某些 Ruby 实现在某些主流性能指标上给 CPython 垫背,妥妥地。 最后,之前围绕展开的是你的其中部分观点,和 Python 其实关系不大,只是主题的关系顺便拿来当例子(虽然也不算躺枪);换其它多数语言,上面很多部分也适用。这倒是正好说明了某些观点对语言设计者存在普遍的危害,所以有必要澄清。 |
147
wizardforcel 2016-08-11 16:10:32 +08:00 via Android
@brucedone shellscript 比 python 快??上数据。
|
148
brucedone 2016-08-11 20:17:00 +08:00
@wizardforcel 我说的是 @YORYOR ,他说换了 python 之后从分钟级别的 shell script 到了小时级别的 python ,这个锅让 python 背,在没有看到具体数据之前,我是不服的。
|
149
serial 2016-08-12 15:47:09 +08:00
@appleorchard2000
不要说些理论的东西,你有用 python 开发过非阻塞系统吗?哪怕是数据量在 1 个 GB ? 有没有想过,猴子补丁和版本维护问题。要知道,做系统是要雇佣人,雇佣设备花钱的。 你的话,让我感觉你只是个学生,并没有面临过“钱”,特别是维护所花费的“钱”的问题。 |
150
serial 2016-08-12 15:51:58 +08:00
@appleorchard2000
我再说的详细一点。最简单的,投票系统,并发量:每秒 1000 次写。那么用 mysql 做用户的元数据持久层,多个 redis 做缓存层,仔细想想你的 python 开发栈怎么构建。你难道告诉我,用猴子补丁来改写 mysql 、 redis 的 TCP 层? |
151
wizardforcel 2016-08-12 17:20:11 +08:00
@serial
这是你的: expression ::= "(" operator operand { operand } ")" operand ::= expression | literal 这是我的: expression ::= operator "(" operand { "," operand } ")" operand ::= expression | literal 别不懂装懂。 |
152
serial 2016-08-14 00:48:27 +08:00
@wizardforcel
你这什么狗屁玩意,拿着 BNF 在这里扯什么淡。我跟你说的是语法树, AST , Abstract Syntax Tree 。 Understand ! 就你这菜鸟玩意,多少年水平也在这跟我晃荡。你写过多少代码量了?玩过 TB PB 数据吗? |
153
serial 2016-08-14 01:06:59 +08:00
@wizardforcel
(* (+ (- 1 2) 3) 4) 的语法树: ........* ......./..\ .....+...4 ..../...\ ...-.....3 ../..\ .1...2 (* (+ (- 1 2) 3) 4),在语法扫描器扫描的时候,由左至右,遇到 ( 就表示一个数据。在 Lisp ,所有内容都是数据,或者说都是列表(这是解释器抽象上的,不是语言语法上的)。() 用来包裹一个列表。每当遇到一个 (,解释器 /编译器就会构造一个分支,层层递归。整个语言以递归构造出整个语法数,因此,这类函数式语言特别注重递归,由此出现尾递归用来优化递归性能 --- 编译成嵌套循环。 你把它写成 *(+(-(1, 2), 3), 4) 再去试试。扫描器扫描到 * + - , 就这么一个小小的表达式,多少个 delimiter 了??? 一个人工智能程序,得要扫描多少个?想过没有?你让机器去生成你这种垃圾代码?你先问问当前的硬件水平够不够速度去挨个判断你这些 delimiter 。 |
154
wizardforcel 2016-08-14 14:52:46 +08:00 1
@serial
我为啥写 BNF ??我问你机器可读性是怎么来的??规则越少越可读!现代编程语言判断多是因为 statement/expression 的规则多。只要不把规则变复杂,谁放前面谁放后面完全无所谓! 你他娘的会分析复杂度吗??你睁大眼睛仔细看看,我的 expression 只有一条规则,也就是说对每个(包括嵌套)的表达式只判断一次,懂吗??我不需要判断 delimter ,这个位置就应该是个 token ,不是就不合法,懂吗?? 你的式子不照样要分析运算符??一开始的确只需要判断个括号就行了,复杂性都在后面,都被你吃了?? 你对编译的理解还处于外行的阶段。只有感性认知,只是知道写成什么样是可读的,但是根本就不知道其中的原理。 会写个 AST 就代表你有本事了?你咋不把我的表达式的 AST 写出来看看一不一样??每个程序员都应该知道的东西就别拿来显摆了。 |
155
wizardforcel 2016-08-14 15:02:27 +08:00
|
156
serial 2016-08-15 08:43:48 +08:00
@wizardforcel
"我为啥写 BNF ??我问你机器可读性是怎么来的??规则越少越可读!" 你是个傻瓜吗?机器要读懂代码,要能理解代码,要能自己生成代码! Understand !不懂就别在这胡说八道。 |
157
serial 2016-08-15 08:45:28 +08:00
@wizardforcel
写过 TM 的语法扫描器吗?你把你那个代码,用 python 语言写一段语法扫描算法,我看看。你给我的感觉,就是个学生,一点像样的项目都没写过。你在公司是干什么的?还是说你压根就是个学生? |
158
serial 2016-08-15 08:48:29 +08:00
@wizardforcel
"你他娘的会分析复杂度吗??你睁大眼睛仔细看看,我的 expression 只有一条规则,也就是说对每个(包括嵌套)的表达式只判断一次,懂吗??我不需要判断 delimter ,这个位置就应该是个 token ,不是就不合法,懂吗?? " 放你的屁,你不判断 delimter ,怎么生成字符流?????? TMB 的写过扫描器吗?看过扫描器原理吗??? 老子真的忍不住骂人了。 你 MB 你家的编译器、解释器不判断 delimter 啊,草你 MB 的,你发明的不需要判断 delimter 的编译器? 傻逼! |
159
serial 2016-08-15 10:14:28 +08:00
@wizardforcel
给你这傻逼写一段扫描器解析代码,自己 TMB 复制到解释器,看输出是不是 ["*",["+",["-","1","2"],"3"],"4"]。代码使用 js 版本,阻止 V2EX 排版导致的缩进问题。完全函数式和尾递归,好好 TMB 去体会。对于人工智能程序,则不是由人来写,而是机器程序自己学习后,自己生成, understand ?机器学习只是初步。 源代码(使用 nodejs 解释器, or chrome 浏览器运行): var text = "(* (+ (- 1 2) 3) 4)"; var at = 0; var ch = text[at]; function next() { if (at++ >= text.length) { throw new Error("text tail out"); } ch = text[at]; } function space() { while (/\s+/.test(ch)) { next(); } } function word() { var result = ""; while (!/\s+/.test(ch) && ch !== ")") { result += ch; next(); } if (at < text.length - 1) { next(); } return result; } function parse() { space(); if (ch !== "(") { return word(); } else { next(); return [parse(), parse(), parse()]; } } var ret = parse(); console.log("%j", ret); |
160
serial 2016-08-15 10:16:34 +08:00
@wizardforcel
输入 (* (+ (- 1 2) 3) 4),扫描后打印出 ["*",["+",["-","1","2"],"3"],"4"]。这就是字符流的构建过程。我看看你 TMB 怎么个"不需要判断 delimiter ",写一个扫描器? |
161
gowk 2016-08-15 10:25:07 +08:00 via Android
哈哈,这都能撕起来,西乔的『神秘的程序员』诚不欺我也!
|
162
FrankHB 2016-08-15 11:51:58 +08:00
@wizardforcel 是,光提性能,垫背的多了。然而你提的这里除了 js 哪个都没有那么作,显然相对比较老实,应用领域比较固定。
js 自己有另外的大波槽点所以在这里不需要怎么强调。 另外你对语言实现的理解确实有些拙计。 分析运算符不需要是验证 grammatical rules 的 parser 的工作——的确“复杂性都在后面”但是对付起来比改 parser 方便太多了(即便绝对工作量不一定小); 复杂度需要考虑,但即便是设计实现 DSL ,通常也不会是需要最优先考虑的事项; 写死运算符要通用容易增加维护成本,不仅体现在语法上,而且影响语义规则的复杂性; 对于通用目的语言,即便只考虑语法,按你大大咧咧的策略,一不小心很容易没法“这个位置就应该是个 token ,不是就不合法”(典型反面教材: C 、 C++、 Haskell ……)。 @serial 其实实际项目确实很多时候不需要写 BNF ……简单的文法都自己手撸 parser 了。 虽然我在项目里钦定语言 spec 的时候还是加了点 formal grammar ,但也得结合项目需求而不是直接套用 BNF 。 而 Lisp (严格来说,仅指使用 S-expression 为基础 syntax 的 dialects )在这里更简单之一正是因为不需要 BNF ,随便什么 CFG 都能应付,简化成纯 S-expr 直接都不需要归纳 grammar 无脑 LL 就行了。 |
163
FrankHB 2016-08-15 12:03:37 +08:00
@serial 你对编译器理解也是有问题的。编译器输入的源语言不见得就得是文本流的形式,比如可以是已经 parse 过的 AST ,这种情况就不需要处理 delimiter (要处理也是代码里自己实现的逻辑)。
大部分主流语言的确要求源代码是文本,但既然考虑 Lisp 就没法直接这样断言了。 |
164
2225377fjs 2016-08-15 12:08:58 +08:00
@appleorchard2000 说的挺好的,不过个人感觉,对于 Python 语言而言,就不要去考虑并行多线程了,虽然看起来是挺美好的,但是,对于绝大多数程序员来说,根本就不能实现正确的并行程序,例如 Java 程序员,其实绝大多数也都是在业务逻辑而已。。。 Python 直接就不支持并行多线程也挺好的,对于大多数系统,业务层好好的设计,用相对独立的模块来实现,减少业务模块之间的交互,这才是正道。。。 linux 身就更强调多进程的结构来解决任务。。
@serial Python 真没你说的那么不堪,起码我在的公司就大规模的使用 Python 来实现后端系统,而且还是游戏后端。。。至于你说的分布式啥的, Python 不是没有,而是你不知道,而且 Python 圈子这方面的东西确实少,跟 Java 没法比。。。每秒几千次请求啥的 Python 做不到。。?有点太看不起 Python 啊。。。。 Python 慢是慢在它本身是一个完全对象化的语言,这点比 Java 做的还要纯粹,而且有没有 jit ,不过话说回来,绝大多数应用场景还没有需要来拼语言速度的,如果最后发现语言速度是问题,多半是采用的技术方案有问题,而不应该让语言来背这个锅。。 |
165
wizardforcel 2016-08-15 15:39:40 +08:00
@serial 代码拿好不谢。
tokenRE = /^[A-Za-z_]\w{0,31}/; numeralRE = /^\d+/; function matchAndReturn(str, re) { var r = str.match(re); return r? r[0]: null; } isToken = s => matchAndReturn(s, tokenRE); isNumeral = s => matchAndReturn(s, numeralRE); function parse(expr) { var r = parseExpr(expr, 0); return r? r[0]: null; } function skipBlank(expr, at) { while(expr[at] === ' ') at++; return at; } function parseExpr(expr, at) { at = skipBlank(expr, at); //literal var r = isNumeral(expr.substr(at)); if(r) { at += r.length; return [parseInt(r), at]; } //call return parseCall(expr, at); } function parseCall(expr, at) { var arr = []; //operator at = skipBlank(expr, at); var r = isToken(expr.substr(at)); if(!r) return null; arr.push(r); at += r.length; //'(' at = skipBlank(expr, at); if(expr[at] != '(') return null; at += 1; //operand 1 at = skipBlank(expr, at); r = parseExpr(expr, at); if(!r) return null; at = r[1]; arr.push(r[0]); //other operands while(true) { //',' at = skipBlank(expr, at); if(expr[at] != ',') break; at += 1; //operand at = skipBlank(expr, at); r = parseExpr(expr, at); if(!r) return null; at = r[1]; arr.push(r[0]); } //')' at = skipBlank(expr, at); if(expr[at] != ')') return null; at += 1; return [arr, at]; } |
166
wizardforcel 2016-08-15 15:53:43 +08:00
@serial
你不就认为'('这个东西能“定界”嘛??我问你定界是为了什么??那好,我让规则尽量少,不是 literal 就是调用,还需要这个定界符干什么? 你也知道 token 解析需要时间,你怎么不说你的 literal 解析还需要时间呢?而且规模一多我就不信你的表达式不抽象成各种过程,到时候+-*/就不够用了,你还得用 token 都命名,这是一百步笑五十步??(手动 doge ) 滚回去做你的工程吧,别忘了下次来的时候带上 IO 的跑分。 @FrankHB 这个讨论是有前提的,也就是“整个程序是一个表达式”,显然通用语言有各种语句,不符合前提。 既然是一个表达式,那么就可以针对性地优化,即完全使用调用表达式把优先级捋直,从而使机器可读。由于它不是 literal 就是 call ,那么我就判断完 literal 就可以钦定。 我的 BNF 也是为了不使用 parser 就能简略表明我的意图。现在看起来也不必要了。 |
167
serial 2016-08-15 17:51:45 +08:00
@FrankHB
拜托,你搞清楚编译器由什么组成?首先,第一步,你要解析。解析的目标是什么,是一大堆文本。更通俗点,是一大堆字符串。你怎么在字符串中解析出对象?没有 delimiter 你以什么作为参照解析? |
168
serial 2016-08-15 17:56:15 +08:00
@wizardforcel
1. console.log(parse("(* (+ (- 1 2) 3) 4)")) 打印出来是 null 2. tokenRE = /^[A-Za-z_]\w{0,31}/; isToken = s => matchAndReturn(s, tokenRE); 使用 JavaScript 内置的正则表达式解析器,要点脸可否,你用别人的 delimiter 解析器,然后说你的扫描器不需要 delimiter Do you 要点脸 ok ? 你的代码就是一坨 shi ,出来大大的 null 。 |
169
serial 2016-08-15 17:58:17 +08:00
|
170
serial 2016-08-15 17:59:19 +08:00
|
171
wizardforcel 2016-08-15 19:14:18 +08:00 via Android
@serial
懒得理你了。 1. 我的代码解析“ mul(add(sub(1, 2), 3), 4)”,不解析你的 sb 前缀表达式 2. 你没用正则? istoken 和 isnumeral 很容易拿 while 来写出来,我就不多说了 3. 你的 literal 怎么解析的??还不是用循环判断非空白字符??那跟我判断字母和数字有什么区别??可笑。 实际情况就是,你的 isblank 和我的 istoken 复杂度差不多,自己好好想想,别他娘的整天自以为是。 |
172
wizardforcel 2016-08-15 19:35:20 +08:00 via Android
@serial
你的表达式括号之前是不用判断,后面是不是还得判断非空白字符??跟我判断字母数字有啥区别??我只不过是把括号和第一个操作数换了个地方,你这弱智就理解不了了。 我就是看不惯前缀表达式,跟你这傻逼说道说道,没想到你丫就是故意来找茬的。 你他娘的没用正则??双标狗爱滚多远滚多远。 |
173
FrankHB 2016-08-15 19:51:01 +08:00
@wizardforcel “显然通用语言有各种语句,不符合前提”显然是错的。语句是 Fortran 发明的玩意儿。典型的 Lisp 就不管你什么语句。实际上语句完全可以通过表达式求值规则等效出来,没有语句的设计明显还更通用一点。
另外你搞混了太多东西。干掉或者不干掉优先级纯粹是语法上的选择,而是不是能钦定规约得看语义规则怎么设计,后者至少不要指望用 BNF 能搞定。如果你知道怎么给出形式语义以使之和语法的形式一致,你就不该纠结 literal 或者“语句”才对。 @serial “解析的目标是什么,是一大堆文本”这个显然也是错的。之前提过了,一般意义上的 Lisp 就不要求输入是文本,这不表示 Lisp 不能编译。还有纯位图的语言呢。连 tokenize 都不必要的情形谈何 parse 。 当然,你可以像 FSF 一样争辩定义“源代码”的“人类可读”的具体形式是指的什么,不过人家是有政治目的的。我不了解技术上存在什么必要理由去缩小“编译器”的外延。 |
174
serial 2016-08-16 10:00:09 +08:00
|
175
serial 2016-08-16 10:02:18 +08:00
@FrankHB
你傻呀,还是傻呀?我请问,你解析什么东西? 不是字符流,是什么? 就连最简单的 0 1 也 TM 是二进制流。 麻烦你先搞懂计算机的常识。 另外,你用别人解析出来的 AST ? 别人都解析出 AST ,还关你个屁事?然后你再去写个扫描器扫描人家的 AST ?一点常识都没有,好不好。 |
176
serial 2016-08-16 10:04:29 +08:00
@wizardforcel
睁开你的狗眼看清楚,什么是正则表达式。傻逼玩意。正则表达式里边有没有 delimiter ?草拟妈的逼,有没有 delimiter ??? 自己用着别人的 delimiter ,瞎着眼睛说没有 delimiter ,你是你妈的逼呀?全局扫描用正则表达式,还用你写啊!!! 吗隔壁不会写扫描器,就别 TMB 装,把你爹写的代码打印下来贴在你家里好好 TMB 学习学习,别 TMB 出来丢你爹的人。 |
177
serial 2016-08-16 10:06:51 +08:00
@FrankHB
纯位图你妹啊,计算机知道什么是纯位图? 你连起码的计算二进制原理都没搞懂,大学还没别业呢? 哥告诉你个常识:纯位图是 TM 人类写的解析器 --- 解析一个文本到协议规范的二进制流。计算机只认 TM 的二进制 0 1 , understand ? |
178
wizardforcel 2016-08-16 10:53:15 +08:00
|
179
bombless 2016-08-16 11:08:36 +08:00
想起来 ruby 有好几个项目想让 ruby 快起来,最终还是没啥卵用
我觉得你们还是要学习一个,给这些开源工具多做贡献,不多试试怎么知道必然会失败呢( |
180
serial 2016-08-16 13:07:37 +08:00
@wizardforcel
不要正则么? 再来个 lambda 表达式? 给你爹跪舔: var text = "(* (+ (- 1 2) 3) (lambda (x y) (* x y)))"; var at = 0; var ch = text[at]; function next() { if (at++ >= text.length) { throw new Error("text tail out"); } ch = text[at]; } function space() { while (ch === '\n' || ch === '\t' || ch === ' ') { next(); } } function word() { var result = ""; while (ch !== '\n' && ch !== '\t' && ch !== ' ' && ch !== ")") { result += ch; next(); } if (at < text.length - 1) { next(); } return result; } function parse() { space(); if (ch !== "(") { return word(); } else { next(); return [parse(), parse(), parse()]; } } var ret = parse(); console.log("%j", ret); // ["*",["+",["-","1","2"],"3"],["lambda",["x","y",["*","x","y"]],""]] |
181
FrankHB 2016-08-16 13:46:17 +08:00
@serial 没专业知识、眼界和历史常识撑腰,读空气无能也得有个限度。
本来这种口水问题见了无非是能渡几个是几个,剩下的我就是当×乎娱乐同等笑话,但既然诚心想当被科普的靶子就没法坐视不理了。 https://en.wikipedia.org/wiki/Esoteric_programming_language#Piet 需要更多干货打脸请自便。 二进制?哦, ENIAC 用的二进制?或者你能钦定 ENIAC 不是计算机? 常识:“计算机”这货 TM 本来就不管你毛线几进制,几进制无非是为了(不限于计算机)实现方便事后诸葛亮出来的, understand ? 还有你想说现在的哪个计算机用的什么 01 ?有点规范意识的话,你该不会还停留在目无 trap representation ,分不清 bit/byte/octet 的层次上吧? 再告诉你个常识,我上面说的位图是指的和光栅图对立的东西,是“位图”这个词的原意。 规范? Device-dependent bitmaps 在 API 以上你想要什么规范?或者你以为别人口中的位图就是 M$的 DIB 还是什么来着? 想用序列化格式偷换概念,还是太嫩了。 |
182
FrankHB 2016-08-16 13:50:21 +08:00
得,撸点 parser 就叼毛自以为造完“编译器”、拿规范偷换概念不给 reference 、目无 ISA 和 ABI 直接 bb 错误组成原理概念的,一概以民科论处。再跳的通通按不尊重现役苦力的阶敌清算。
非技术话题不再这里废话。 |
183
FrankHB 2016-08-16 13:56:54 +08:00
哦,抽了缩水了。更正:
我上面说的位图是光栅图的实现,是和矢量图对立的东西。 |
184
xuweitiger 2016-08-16 14:32:00 +08:00
为啥好好的话不能好好说,看来“大神”都是神挡喷神佛挡喷佛啊,真是见识到了……
|
185
serial 2016-08-16 16:08:53 +08:00
@FrankHB
你知道有个东西叫做 “编译器”? 你知道你的那些什么狗屁位图程序最终都要编译器编译成二进制 0 1 ? 学 python 学傻了,没事学点 C 语言。一点计算机常识都没有。 简直是对牛弹琴,连入门都没有。难道要我跟你从字节、页、映射开始讲起? |
186
serial 2016-08-16 16:12:20 +08:00
@FrankHB
> "常识:“计算机”这货 TM 本来就不管你毛线几进制,几进制无非是为了(不限于计算机)实现方便事后诸葛亮出来的, understand ?" 傻逼一个! 计算机用二进制是因为二进制对计算机来讲只有两个符号 0 和 1 ,是最简单的实现。这点常识都没有,真是个傻逼! 也真操了,这种傻逼说起话来还觉得自己挺懂一样。我再给你普及一下:计算机不只只用二进制 0 1 ,而且只懂加法,不懂减法、乘法、除法。明白了,菜鸟! (所有的其他运算通过移位、补码、溢出进行换算) 和你讲话真 TM 累!你不会连大一学生 C 语言的普及入门课程都没有上过吧。 |
187
palxex 2016-08-16 16:37:35 +08:00
@FrankHB 该 B 就 B 吧。跟一个在到了 C 底层就认为再往下都是神圣不可动摇领域的有啥好讨论的么。这行也到了要靠老一代自己死掉来解决争端的时候了。
|
188
FrankHB 2016-08-16 19:18:48 +08:00
@serial 你可知道你有资格钦定什么叫编译器?上面的东西光是喂鸡都够打你多少脸了。(不是我给的,自己翻什么叫 compiler 什么叫 source code ,包括 FSF 的观点上面都有提。)
还“两个符号 0 和 1 ,是最简单的实现”——你特么实现过计算机?没做过别在自己不了解的领域瞎 bb 。 还有……学 C ?这是我这几天看过最好笑的笑话。你不是 C 撸傻了吧,哪来的蜜汁自信在我面前提 C ?我给人校对 C11 和 N1570 的时候咋没发现 C 给你钦定什么 0 什么 1 了。(我看就你这态度这辈子是不用指望给 WG14 舔屁股了……) 看样子你自认为很懂 C 嘛。来来来,给我解释下, ISO C 对 basic source character set 跟 basic execution character set 分别钦定的理由?按你说的 0 和 1 就够的东西,为什么需要那么复杂?是不是说明 C 是辣鸡? 可惜下面“所有的其他运算通过移位、补码、溢出进行换算”就露马脚了。首先浮点打脸啪啪啪。然后就算是整数,哪个逗比 ISA 会拿这种东西“换算”?哦,强行说 C 是嘛,也行: WG14 N1570 6.2.6.2/2 For signed integer types, the bits of the object representation shall be divided into three groups: value bits, padding bits, and the sign bit. There need not be any padding bits; signed char shall not have any padding bits. There shall be exactly one sign bit. Each bit that is a value bit shall have the same value as the same bit in the object representation of the corresponding unsigned type (if there are M value bits in the signed type and N in the unsigned type, then M ≤ N). If the sign bit is zero, it shall not affect the resulting value. If the sign bit is one, the value shall be modified in one of the following ways: — the corresponding value with sign bit 0 is negated (sign and magnitude); — the sign bit has the value −(2M) (two ’ s complement); — the sign bit has the value −(2M − 1) (ones ’ complement). Which of these applies is implementation-defined, as is whether the value with sign bit 1 and all value bits zero (for the first two), or with sign bit and all value bits 1 (for ones ’ complement), is a trap representation or a normal value. In the case of sign and magnitude and ones ’ complement, if this representation is a normal value it is called a negative zero. 我甭管你的“补码”脑补成什么词性,告诉我你那坨玩意儿怎么兹瓷 padding bit 跟 trap representation 跟 negative zero 啊,蛤? 跟你说话真 TM 累,你不会连大一学生 C 语言的普及入门课程十有八九是冒牌得被我婊了几年了都不知道吧? |
189
FrankHB 2016-08-16 19:24:51 +08:00
@palxex 到了这份上就跟某人就不叫讨论而是单方面教育了。
除去科普意义依旧,这几天我成天撸比下有余的“底层”的东西(倒腾 opcode/modrm/sib/disp 之类)正不爽呢,正好换换口味……贴吧里矫正了点汇编=底层厨然而毫无波动,果然还是没有狠狠干几个自以为很懂 C 的顺手。 |
190
kamil 2016-08-17 02:34:28 +08:00 via iPhone
@xuweitiger 神挡喷神佛挡喷佛,哈哈,手动赞
喷子喷的再多,还是个喷子,明白人一看自然高下立判,就像那段 c 风格的 js ,明显两种都没玩好,一瓶不满半瓶晃荡,还好意思出来卖弄只能说明脸皮厚 一次能 B 好多无脑喷子及傻逼跟风狗,这就是这个帖子的作用 |
191
wizardforcel 2016-08-17 10:09:22 +08:00 via Android
@serial
[计算机用二进制是因为二进制对计算机来讲只有两个符号 0 和 1 ,是最简单的实现。 ] 是因为数字电路只认高电位和低电位,是最简单的实现。图灵机,或计算过程随便用什么进制都可以。人类也能够计算,你平常拿二进制计算嘛? naive 。 你的计算机很难让别人知道指物理实现还是概念模型。智障还是故意偷换概念?? [ 计算机不只只用二进制 0 1 ,而且只懂加法,不懂减法、乘法、除法。 ] 计算机懂加法??滚回去看加法器是怎么实现的。乘法器的确拿加法器实现,但加法器也不是凭空变出来的。 和你讲话真 TM 累!你不会连组成原理都没学过吧。。。 |
192
wizardforcel 2016-08-17 10:19:16 +08:00 via Android
@serial
另外你最好拿本编译的书看看 delimiter (定界符)是啥意思。 你说的+-*/叫 operator (运算符),不叫 delimiter 。 我的 add 、 mul 叫做 token (标识符),也不叫 delimiter 。 delimiter 这种简单、固定的东西有利于机器扫描。你的式子前后那个括号就属于 delimiter 。 我需要扫描 token ,那是必然的。不扫描还能写啥 parser ?你的式子就只需要 delimiter ,不扫描其他的东西?? 真是无知。 |
193
serial 2016-08-17 11:25:11 +08:00
@FrankHB
你连 TMB 编译器最终编译成二进制都不知道,提 NMB 的编译器。好好睁大你的狗眼看清楚了: “在数学和数字电路中,二进制( binary )数是指用二进制记数系统,即以 2 为基数的记数系统表示的数字。这一系统中,数通常用两个不同的符号 0 (代表零)和 1 (代表一)来表示。以 2 为基数代表系统是二进位制的。数字电子电路中,逻辑门的实现直接应用了二进制,因此现代的计算机和依赖计算机的设备里都用到二进制。每个数字称为一个比特(二进制位)。” 傻逼一个东西,你 TMB 一看就是没上过大学的菜逼。大学课堂哪怕是选修课,都有信号、电子电路的介绍。你 TMB 连与非门都没听说过吗? 知道与非门是什么吗?傻逼! 0 1 直接映射与非门的 “与” 和 “非”,懂了吗? 傻逼! 要不要老子给你贴贴三极管、放大器的电路图。 大学都没上过,在这里瞎逼逼你麻痹。 |
194
serial 2016-08-17 11:26:56 +08:00
@ kamil
你是你妈的哪条狗,递归都不懂,出来秀你妈的逼。不懂递归,难道 TMB 不懂百度谷歌? |
195
serial 2016-08-17 11:29:40 +08:00
@ wizardforcel
> “另外你最好拿本编译的书看看 delimiter (定界符)是啥意思。 " > "你说的+-*/叫 operator (运算符),不叫 delimiter 。 ” 你就是个傻逼, /^[A-Za-z_]\w{0,31}/ 这就是你这傻逼写出来的。你觉得这是运算符的意思?自己打自己的脸。你 TMB 自己写的解析程序都搞不懂原理? |
196
serial 2016-08-17 11:31:31 +08:00
@wizardforcel
“计算机懂加法??滚回去看加法器是怎么实现的。乘法器的确拿加法器实现,但加法器也不是凭空变出来的。” 加法器是 NMB 怎么变出来的? 你来说说计算机不懂加法,怎么做加法。傻逼。 |
197
wizardforcel 2016-08-17 12:04:32 +08:00
@serial
[加法器是 NMB 怎么变出来的? 你来说说计算机不懂加法,怎么做加法] 你来说说计算机不懂乘法,怎么做乘法。乘法器拿加法器实现,计算机怎么就不懂乘法了??懂你 MB 。 [你觉得这是运算符的意思?自己打自己的脸。你 TMB 自己写的解析程序都搞不懂原理?] 谁给你钦定“必须用运算符”了??懂啥叫调用表达式和标识符嘛?? 程序大了你不定义子过程??定义了子过程之后你的运算符够用?? 不懂就玩你的前缀表达式去。 [不懂递归,难道 TMB 不懂百度谷歌?] 尾递归允许多个递归调用??你这“[parse(), parse(), parse()]”叫“出现在函数末尾”? 来来来,你手动把你那三个 parse()给我展成循环。 |
198
serial 2016-08-17 12:36:34 +08:00
@wizardforcel
卧槽,你还真 TM 弱智啊。计算机不懂乘法,当然是靠加法移位+溢出+补码啊,这种常识都不知道。百度下谷歌下总可以吧。 你也没上过大学? /^[A-Za-z_]\w{0,31}/ 这 TM 哪个是运算符? |
199
serial 2016-08-17 12:37:17 +08:00
@wizardforcel
扫描你妹的 token ,连 TMB 的 token 是什么意思都搞不清楚,还逼逼扫描器。记清楚了,扫描器扫描出来后,生成 token 对象,一般用元祖 (id, symbol-table) 记法表示。在 C 中一般用数组 + 指针实现,高阶语言可以使用 tuple + object / hashtable 实现。你就是个屁都不懂的傻逼。 哥给你贴上 <<Compilers: Principles, Techniques, and Tools>> (龙书) 第 5 页的原文,好好学习: @FrankHB 还有你这个傻逼,看清楚了 “ the stream of characters ”: --> The first phase of a compiler is called lexical analysis or scanning. The lexical analyzer reads the stream of characters making up the source and groups the characters into meaningful sequences called lexemes. For each lexeme, the lexical analyzer produces as output a token of the form program (token-name, attribute-value) that it passes on to the subsequent phase, syntax analysis. In the token, the first component token-name is an abstract symbol that is used during syntax analysis, and the second component attribute-value points to an entry in the symbol table for this token. <<Compilers: Principles, Techniques, and Tools>> --> |
200
FrankHB 2016-08-17 12:37:37 +08:00
@wizardforcel 文法元素取决于语言,编译本身决定不了。看编译书也不会给你说清楚。
token 在此一般翻译成记号。标识符(identifier)是 token 的一种。而 delimiter 通常特指(中缀)分隔其它 token 的字符序列,本身不一定是 token 。 括号也可以设计成 punctuator 作为 token 的一种,但因为需要检查匹配所以一般不会做成 delimiter 。 operator 可以设计为 puncutator 也可以直接作为 identifier 。 @serial 我搞不懂是你语文水平拙计还是编译常识拙计了。 谁 TMD 教你编译只能是数字电路实现的? 谁 TMD 教你编译目标必须是二进制的拎出来,找找看中央编译局的同志是不是能怼死他。 逻辑拙计就算了,提数字电路还就会拎 gate level ,跟贴吧一个拎着卡诺图设计 IC 的逗风格真像…… 看你组成原理常识都没的样子,我就不指望你听说过 RTL 了(不管是编译器里的那个还是 IC 设计里的那个)。 先进化到突触的延迟满足得了跟得上 bb 的内容是啥再扯蛋吧。一问三不知转移话题 yy 强行替人没上大学,一绕回去还不就没词儿怂了。我考察你的 C 的问题呢?咋不把 ENIAC 开除出“计算机”啊? 就你这反应要来面试,时序约束就炸了。 似乎你不仅懂 C ,还懂递归?来来来,给我把 C 的语义用μ-recursive function 撸一遍? |