关于翻译:译证明你可以做困难的事情

原文地址:Proof You Can Do Hard Things原文作者:Nat Eliason本文永恒链接:https://segmentfault.com/a/1190000044187657译者:Fw恶龙校对者:Fw恶龙当一个富裕好奇心的学生问为什么须要学习微积分时,你会怎么答复? 你也晓得只有一些非凡职业会应用到微积分,而大部分人在成年后是根本用不到的。 你能够对他说:“这对你上大学有帮忙”,但他们仍会想晓得为什么大学会在意你是否懂得微积分。而如果他们曾经到了大学,兴许你会说:“这能够帮你找到一份好工作”,然而为什么企业的老板会关怀你会多元微积分呢? 我就是这些烦人学生中的一员。我从没有听到过有谁对这一问题进行很好的解释,以至于只管我能够获得优异的问题,但从来不关怀是否可能获得好的问题。 我仍不明确辛苦地学习那些在事实中永远不会应用到的常识有什么意义,我认为能够不必认真对待这些课程,只做最根本的要求,而后还可能顺利毕业。 但我最近意识到学习微积分是为了证实你能够解决艰难的事件。 你可能为本人或你的孩子造就的最有用的能力或者就是能够解决艰难事件的能力。证实你有能力做到这些,将会是你简历中很好的一个竞争劣势。 咱们对待本人的形式是由新近所取得的能力组成的。你越强制本人做艰难的事件,就会发现自己越有能力。就好比你如果可能跑齐全程马拉松或可能举起两倍于本身体重的杠铃,那么因为关照新生儿而给你带来的睡眠不足对你而言也只是小事;如果你善于有机化学或计量经济学,那么你将轻而易举地找到一份金融相干的工作。(此处是指在你解决了更加艰难的事件后,一些原本会对你造成困扰的事就显得轻松许多) 但如果咱们始终以回避的态度面对艰难,那么在面对任何一点有挑战性的事件时对你而言都会变得难以克服。咱们会因为一些小事就到某音上哭诉。咱们会发现自己无奈学习新技能、从事新职业、解脱蹩脚的处境。而证实你能够做艰难的事件,是你能够送给本人最好的礼物之一。 我会尽可能防止对孩子们扯谎,因而我不会通知他们学习微积分是如许重要的一件事,也不会通知他们问题有如许重要。事实上,学习微积分只是用来证实你有能力去做艰难事件的一个形式,但如果你自身就曾经在做艰巨的事件,例如在业余时间学习编程和构建应用程序、博得一场足球比赛、写一部小说……那么微积分对你来说就不是很有必要,你所做的事件可要比它难上许多。 这也是为什么在“问题优异的学生通常会受雇于问题较差的学生”这句话中存在着很多幸存者偏差和谬误倡议的起因。事实上,大多数问题较差的学生并没有去做其余艰难的事件来代替学校的作用,他们只是在鸿鹄之志,所以最终大部分只能受雇于问题优异的学生。 但有些学生学习成绩较差是因为他们沉迷于其余艰难的我的项目。随着工夫的推移,这种对于谋求艰难指标的执着让他们超过了同龄人(Excellent Sheep)。所以,如果你有一个问题较差的学生,而他正执着于一些艰难的事件,你大可不必放心。但如果他们正在嗑high了,看抖音,那就蹩脚了…… Excellent Sheep「卓越的绵羊」:这个词组出自威廉·德里斯科尔(William Deresiewicz)的同名书籍,用来形容在高等教育零碎中,过分谋求问题和传统胜利规范,不足独立思考和真正趣味的学生。这个词组常被用来讥刺过于刻板的教育体制和对于标准化胜利的适度谋求。我并不会特地关怀孩子们上学后的问题。我真正关怀的是他们是否能够向我证实有解决艰难事件的能力。如果他们想用微积分来证实,那没问题。但还有很多很多其余抉择。 如果你不晓得本人是否能做艰难的事件,那就想方法向本人证实这一点。或是养成一个好习惯,或是学习一项技能,或是发明一些货色,无论是什么,都能够让你在面对挑战时的态度从“看起来很难”变成“我能解决”。 去证实你可能做艰难的事件吧。

September 6, 2023 · 1 min · jiezi

关于翻译:译-你的软件可以从ATM机的巧妙设计里学到点什么

原文链接: https://www.simplethread.com/your-software-can-learn-a-lot-fr...为良好失败设计回顾一下你最初一次应用ATM,很有可能,你左近就有一台,你应用ATM机的次数,你本人都数不清。 你从ATM接管到过数目不对的钱嘛? 我猜的答案肯定是没有过,只管ATM机每年存取数百万的钱。只管解决像发放正确数量现金这样的简单工作,ATM机的可靠性证实了设计的独创性。ATM机是如何实现高牢靠的。可能你会感到十分诧异,答案是相当简略的。ATM机采纳了一个比较简单的机制,从一叠钞票中一张一张的抽出,而后在通过时验证他们的厚度。如果抽取的钞票比预期的要厚,很可能是因为多张钞票粘在了一起,取款就会被回绝并留下来供人工查看,这个零碎并不完满,它为良好失败所设计。 那么这与软件设计有什么关系呢?ATM给咱们提供了好多贵重的教训: 执行工作验证工作验证工作执行失败,进行和再次尝试,这种设计的美好之处在于其简洁性,ATM机不是通过创立简单的机制来确保百分之百的可靠性,而是被设计为优雅的解决故障。这是软件开发者须要用心记下来的一节课。为了构建牢靠的零碎,上面一些步骤需遵循。 被许多开发者所忘记的准则为了建设高牢靠的零碎,你须要: 执行繁多、可验证的工作验证工作的执行后果如果验证失败,就撤销可撤销的操作,告诉相干人员。上面设设计软件中,也须要防止一些做法: 下图是软件设计中须要遵循的实际 在验证之前不要做多个操作: 放弃简略, 验证每个步骤。防止为了清理删除一些货色: 相同,隔离有问题的文件能够保留状态信息,这会帮忙咱们解决问题。不要主动去修复问题: 除非你对问题确信无疑,否则编写错误处理代码可能会引发意料之外的问题。失败的时候要有日志记录或状态隔离: 当呈现谬误时,收集数据,并将其搁置在已知的谬误地位。不要在共享地位执行操作: 如何可能呈现故障,在一个长期区域先执行操作。简略,验证,无效的失败解决简而言之,软件设计哲学该当和ATM设计相似,崇尚简略,确保验证,无效的解决失败。 软件的牢靠并不需要借助简单,他须要领有良好的故障解决能力。让咱们持续从物理工程外面汲取灵感,在软件设计中记录这个无效的教训。 上面是文章的评论读者Nicholas Piasecki的评论:如果技术人员调换了10美元和20美元 墨盒,那么ATM机就会给出谬误的金额,这揭示了一个令人丧气的事实,就是总有出错空间。因为我遇到过一次这样的状况,我立即走入了左近的一家分行,退出了丧气人群的队伍,他们都不太高兴,设想一下,被扣掉80美元,但只拿到了40美元。(有些机器曾经通过只有20美元来解决这个问题) 但这依然是一个不错的倡议,就像红绿灯一样,如果工夫谬误导致两个相同方向的的红绿灯都呈现了绿灯,那么保险丝会被熔断,导致所有的信号灯都进入到闪动的“黄灯/红灯模式”。很多软件都能够采纳相似的保险丝技术。他放弃了持续运行,直到有人来查看我为止。然而可怜的是,这种故障爱护在软件中意味着“解体”而不是降级。 尤其是故障很少产生时,你能够间接点重试,而不是编写很少应用的异样代码。 读者mat roberts的评论:我已经在ATM机外面取出谬误的金额,只有一次,那是一个硬件问题--纸币又旧又皱,不晓得是什么起因,在机器外面被卡住了。作者回复Nicholas Piasecki: 这就是互联网,我晓得我一发这篇文章就能收到1000条从ATM机外面取到谬误金额的评论。Jonathan Pryor的评论: 总结这篇文章就是放弃简略,放弃简略,放弃简略,放弃简略。哦,还是放弃简略(因为放弃简略而容易验证),这也是咱们为什么要有开发者准则,不要捕捉异样(Exception 异样的基类),除非前面跟着软件的退出,因为它不简略而且无奈确定具体捕捉的是什么异样(比方OutOfMemoryException),程序是否平安的继续执行。 放弃简略、愚昧,如果咱们认为很厉害,咱们曾经失败了,咱们须要时刻揭示本人咱们并不聪明,这样做,咱们将确保咱们的软件能够被普通人所了解,这对咱们都有益处,因为咱们都是凡人。 读者Al Tenhundfeld的评论: 这与Erlang的“让其失败”准则有些相似,只管不完全相同。指标是尽量避免失败复杂化你的设计。 我没有应用Erlang工作过,但据我理解,Erlang在解决故障的时候应用了一种乏味的繁多职责办法,你不会在你的畛域实现外面混淆大量的谬误弥补代码。相同,你有监督过程来察看你的实现过程,并决定在失败的时候做些什么? 这样做对嘛? 另外,这也让我想起了测试驱动开发取得的很多有价值办法中的一个。当我实际TDD的时候,我发现更偏向于思考代码应该如何失败。而且,我常常可能从新设计API,使其不对谬误进行弥补,而是不容许谬误状态的存在。 作者对Al Tenhundfeld的回复: 你说的很乏味,因为整个文章都变成了关于软件故障的内容。我批准,在大多数状况下,用错误处理来净化你的代码只是一种节约。通常状况下,更好的做法是花工夫记录日志,这样当谬误产生的时候,你就晓得产生了什么,并且可能通过修复软件进行弥补,而不是让软件经验繁琐的操作来尝试自我修复。

June 16, 2023 · 1 min · jiezi

关于翻译:译-我仍然喜欢编程的十件事续

前言,这个翻译系列有点让我骑虎难下的感觉,原本打算翻译软件破绽呢,然而发现这篇文章更乏味,于是就决定先翻译这一篇。 原文: https://www.simplethread.com/... 这篇文章是我依然喜爱编程的十件事的续篇。 11. 学习新工具软件工程在许多方面依然是不成熟的。这意味着咱们的工具会一直的倒退。对于旧有的问题人们一直给出新的办法去解决。在加上来自于程序员喜爱的懈怠驱动式翻新,你就会有各种各样的实用工具和开源解决方案去简直解决任何问题。我在DIY(Do it yourself 本人动手做)畛域有过相似的感触。比方,在木工畛域,你能够应用电锯手工管制角度,只有你有足够的毅力和急躁就能做的很好。而后你的木匠敌人带着它的斜切锯来了,哦,天呐(原词为 oh,man,译者认为这里表赞叹,所以未采取直译),这些角度就变得如此容易切割,那么润滑,看起来那么丑陋。如果你和我相似,那应该也了解,那是一种十分让人满足的体验。如果你认真思考,编程也能够给你很多机会找到适宜当前任务的工具。 12. Helping people 帮忙他人我以前写进入软件工程畛域是因为对视频游戏感兴趣,并且驱动我去了解视频游戏背地产生了什么。尽管这是真的,让我继续对技术放弃趣味的另一部分起因是齐全的掌控感。这是四周大多数人都不了解的货色,而我能够帮忙他们了解它。是的,这意味着有的时候要为敌人或家人做IT反对。是的。当人们的冀望不统一可能就会感到腻烦。这实用于大多数技术业余,我置信医生、机械师、建筑师、律师也会有一样的感觉,每个人必须设定界线。归根接地,我很开心有一项技能能够帮忙他人。即便我偶然不得不把手举起来说: “道歉,我不晓得你的打印机出了什么问题,然而你否试过重启” 13. Practicing magic 修习魔法我年老的时候浏览过大量奇幻书籍。直到现在我依然空想成为一名巫师,领有说出正确咒语或执行正确的步骤就能扭转四周世界的能力。我想平凡的作品能够做到这点,然而我素来没写过任何被我认为是魔法的货色。最靠近感觉本人像是施展魔法的时刻是构建软件,特地是在DevOps方面,如果你编写了正确的Ansible 脚本或 Terraform 打算,忽然之间一整个协调的服务器队列都在依照你的志愿运作。如果你天天做DevOps,就会让你丢失兴奋感。但对于我来说,这是非常少有的让我看到所有东倒西歪,就像施展魔法般让人欢快,令人沉醉。 14. Turning ideas into reality 将想法变成事实。咨询业最酷的事件是有机会帮忙很多人建设他们独自建设不了的产品-远远超出他们原有愿景的产品。他们可能有一个产品的想法,或者他们对问题有充沛的理解和信念,认为肯定有更好的形式(办法)。不论怎么样,咱们有能力让他们产生,把形象的想法变成事实世界中的人可能从中受害的货色。我永远不会厌倦领导客户实现这个过程,即发现他们真正想要实现的指标,而后帮忙他们将这些想法变成一个真正的、无效的零碎。 15. Diagnosing ridiculous Heisenbugs 修复一些匪夷所思的bugHeisenbug是一个对于bug的术语,当你试图去找起因并且复现时无奈复现。它也是随机呈现并且没有显著谬误的bug 的通用缩写。大多数软件中的bug都是间接的,比方没有思考过的状况,没有思考的上游效应等。然而有些时候,你碰到这样一个问题,始终找起因,然而依据代码推断基本就不可能有这样的行为。然而在你放弃之前,老天啊,你象征地重现了它。这时你就晓得你要学点好货色了,尽管它可能并不令人欢快。他可能是一个谋杀案,而你就是凶手。这个bug会填补你的常识空白,进步你的专业知识程度。例如,通常状况下,如果磁盘有空间并且过程有权限就能够向磁盘写入文件,除非它是一个旧的FAT32(文件系统)的磁盘,你正在向外面写入一个65k大小的文件,因为清理过程,写入会偶然失败。这对我集体来说是一个特地苦楚的教训。 16. Solving impossible problems 解决一些不可能的问题在软件工程畛域,如果有短缺的工夫和精力,简直任何问题都是能够被解决。这既让人兴奋也让人困倦。有时人们会要求你去做不可能做的事件,有时答案是不行,技术还不存在。但更多时候,答案是,这依赖于你进行头脑风暴和钻研,和共事进行白板会议。你经常就能相处一个以前你感觉不可能被解决的解决方案。提醒: 不可能的解决方案通常是通过找出如何将问题从新定义为可解决的问题。而后,你再解决这个问题。一个常见的例子是,如果实际上不能让一个操作变得更快,但兴许咱们能让用户感觉更快。我也曾涉足其余畛域,但没有什么能与编程等量齐观,因为我常常面对那些感觉不可能解决的问题,但最终还是找到了解决办法。 17. SQL我非常高兴NoSQL静止稍稍消退,称为另一个版本的正确工具。我钻研过很多备选的数据库,他们在特定状况下表现出色。例如我喜爱的Redis和Elasticsearch。然而,我依然认为关系型数据库是很棒的通用数据存储--它们是鲨鱼而不是恐龙。(译者著: 鲨鱼和恐龙是同一个时代的物种,这里译者认为是在暗喻关系型数据库在不断进步,适应时代。)我喜爱 PostgreSQL和其余关系型数据的一个很大起因是SQL,是的,它由一些奇怪的不统一之处,并不完满,然而一旦你把握了基础知识,SQL就是一种查问和解决数据集的漂亮直观形式。 在不同的键上连贯多个数据集,做聚合、分组等,为缺失的值提供默认值。当然,你能够在Ruby、Python、C#中做所有这些事件,并且成果也不错。然而在SQL中更加污浊,或依据数据集的值来更新一个数据集的属性? 是的,我将放弃我的SQL技能敏锐,非常感谢。 18. Psychic debugging 通灵调试零碎存在着一个奇怪的问题? 是DNS的问题吗? 不是? 你确定? 好吧,是字符编码的问题? 你确定你没有从Word中粘贴一些智能引号? 兴许是缓存没有正确。在这个畛域曾经有一些念头了,一个乏味的事件就是某些乏味的事件就是某些问题会一直呈现。我曾经数不清有多少次同时形容了一个问题。我在对于代码无所不知的状况下就能准去地诊断出这个bug。这相对让人感觉是某种神秘的、通灵的力量,但其实只是我犯了很多谬误,看到了很多奇怪的货色。 顺便说一句,在那时,问题永远都在DNS上,即便是你曾经齐全排除了DNS。 19. Refactoring 重构重构指的是改良软件的内部结构,同时保留它的可察看行为。但更一般地说,它是指在个别不扭转性能的状况下,清理代码。 一些开发人员认为这很乏味并且浪费时间。但这个观点是错的,道歉,我认为重构是编程中最大的乐趣之一,特地是在长时间工作在一个零碎下的时候。在生活中,你有多少的机会修改过来的谬误? 你有多少机会从新扫视过来的决策并更新它们以适应一直变动的环境。对我来说,并不是很多,并且可能进入凌乱的代码局部,构想如何更清晰地组织它,而后井井有条地进行合成和去模糊化时,你会感到无比的满足。当你实现重构后,测试依然通过,所有工作都和以前一样,但它不再是代码库中人们胆怯批改的局部。 20. Being cool under pressure 在压力下放弃沉着最初一点这并不是编程独有的,但这是我常常体验到的状况。在我职业生涯的晚期,我和一些高级开发人员一起工作,他们在压力下放弃平静。 你会从一个重要的客户那里收到一份奇怪的生产错误报告。数据隐没了? 这到底是怎么回事? 你会开始钻研这个问题,并且胃里有会有一种沉甸甸的感觉.....哦,事件十分不对劲,肾上腺素开始跳动。所有都十分蹩脚,你拉进领导开发人员开始缓和地讲述你所做的所有。他们让你说完,而后平静地回到结尾,兴许走到白板前。 “好吧,让咱们从列出咱们晓得的货色开始,而后咱们能够列出.....”等等。你从惶恐不安到感觉所有都会好起来。有这样人在你的团队真是一种解脱。不知何故,只管困难重重,我想我曾经胜利地在很多软件工程背景下成为这样的人。我能够排汇团队中人的压力,并散发回一种平静的感觉。这并不是说我有什么非凡的能力;我只是看到了足够多的软件问题,并设法找到了正当的解决放那。所以我置信不论这个新的危机是什么,咱们都能度过。可能为咱们团队中的人提供平静的起源十分让我开心。 ...

January 31, 2023 · 1 min · jiezi

关于翻译:译-仍然让我喜欢编程的十件事

原文来自于: https://www.simplethread.com/...这篇博文的灵感来自于我的联结创始人Justin的《编程20年我学到的事》,我没有在这个列表增加太多,然而读了这篇文章之后,我发现我在想所有依然让我喜爱编程的事,即便做了20年。上面是带给我高兴的10件事。 近些年,我的日常工作次要是领导和治理,更多的在于为他人发明胜利的环境,但在这个清单外面,我次要关注的是作为一个开发者编写代码和在生产零碎上的方方面面。不论你的编程教训如何,我心愿上面这些可能让你共鸣。享受吧(enjoy) 1. 等等,这怎么做到的(或者这是为啥呢)这是开发者的特地体验,我从未在编程之外的畛域领会过。你正在钻研一些问题,探寻其逻辑实现,因为它与预期不合乎。但事实上,你越看,你就会越感到困惑...这个零碎怎么会进去正确的后果呢?我十分喜爱这一刻,因为这意味着你行将获得一些冲破,学到一些十分重要的货色。这有点像科学研究的老话: “科学研究上最令人兴奋的短语,那个预示着新发现的短语,不是我找到了它,而是乏味 ”(译者注: 突破性发现往往来自于意料之外的发现,在问题解决中,遇到意料之外的事件并不是好事,反而会有新的冲破发现)。 2. 一个精心设计、保护良好的阐明文档你是否有过这样的经验,走进敌人的厨房,所有都很清朗? 厨具摆放的东倒西歪(荒诞不经),须要的一切都在手边并且容易发现。这很像我进入一个代码仓库,仓库外面有一份设计良好的readme带给我的高兴。readme的好坏依赖于我的项目自身,应该包含清晰的先决条件阐明和根本装置命令、可能会遇到的问题以及解决方案,取得初始数据的一些简略命令,运行测试等等。如果我可能轻松运行该仓库中的代码并基于此进行开发,很少有其余货色可能像设计良好的Readme来博得我的尊重。 3. 测试用例通过 软件开发中一个十分酷的事件是咱们可能从咱们构建的逻辑中立即失去反馈,并且测试过程实质也是能够自动化的。当然,也不是所有的事件都能自动化。比方UX反馈通常须要用户参加,并且咱们并不需要对每一行代码都进行可靠性测试,这破费的代价会有些大。 尽管如此,你依然能够在写下代码之后,写一个测试来验证在设定条件下它是否具备你冀望的行为。它也不仅仅是即时的,你也能够保留这个测试,并在其他人应用的时候提供应用阐明。这十分酷。我的大多数喜好,反馈周期都比拟长。比方烘焙和园艺,可能须要几天甚至几个月能力失去反馈。而且即使如此,解释后果并猜想出错后果依然须要肯定水平的专业知识。但我永远不会厌倦的是,运行测试样例并且看到所有的测试样例通过。 4.rails new译者注: rails new 是一个 Ruby的一个脚手架,rails new,咱们通过几个命令就能够搭建起一个简略的Rails我的项目。 是否有过一个我的项目开始构建时的兴奋劲? 我喜爱那种从零开始,有着有限可能感觉,没有技术债的我的项目。这也是我到当初依然喜爱通过Darden’s i.Lab(一个征询网站)来向年老的企业家提供倡议的一个起因,也是我在20年当前依然喜爱征询的起因之一。你能够在许多事件上取得这种感觉,但软件有些不同,咱们更强调自动化和框架。几个命令,砰!你就取得了一个利用的脚手架,你能够应用这个脚手架来实现你的想法。丑陋。当我盯着一个空白页面的时候,我时常会心愿有一个这样的脚手架用来合作。 5. Vim译者注: Vim是从vi倒退进去的一个文本编辑器。其代码补完、编译及谬误跳转等不便编程的性能特地丰盛,在程序员中被宽泛应用。和Emacs并列成为类Unix零碎用户最喜爱的编辑器。 译者注: 没认真用过,不做过多评估。 还须要我说吗?如同年轻人说的, 如果你用过你就晓得了(原文是一个缩写: IYKYK If You Know You Know)。实际上,我爱的不肯定是Vim,我次要比拟喜爱Vim的模式编辑。简略的说,在Vim中你能够在两种模式下切换,一种是插入,一种是管制文本。在管制文本模式下(也称失常模式)下,你能够通过三个字符删除逗号之前的所有字符。一旦你习惯了,你就再也回不去了。在我本人的电脑上,我甚至将shell也设置为了vi模式,这样我就能够应用一些组合按键来操作命令行参数。如果你没筹备好,那么面对Vim就会很困惑,如果你有了充沛的理解,那么就会发现它很弱小。 6. Building Our Tools 构建工具说到Vim... , 在懒惰的周日下午,我喜爱看木工节目,如The Woodright's shop. 主持人在节目中探讨古时候的工具和木匠的历史。他们通常从根本工具开始,比方手锯和凿子,而后向你展现如何制作一个你本人的工作台。甚至有时是一个简略的车床或磨床。这很吸引我,常常让我想到软件开发。咱们用来编写代码和构建软件的工具也是由代码来构建。咱们的工具个别由其余程序员发明,如果有工夫,咱们齐全能够本人构建工具出像Vim这样的工具来满足咱们的需要。构建工具十分让人满足,在社区方面也十分的弱小。因为它发明了一个自动化反馈循环,有助于减速软件崛起、 7. Open Source 开源在某种程度上,构建工具只是我酷爱开源软件的一个方面。也有人说过开源软件对于集体、公司乃至于整个人类文明有着不堪设想的价值。我只想说开源真他妈的酷。除了维基百科,我想不到其余的,开源软件是人类已经尝试过的最大常识合作我的项目,而程序员处于这个我的项目的最后阶段。如果人类文明在100年内、1000年、10000年后依然以某种可辨认的形式存在,那么我预计咱们依然会有某种模式的数字指令合作集,也就是开源软件。我只晓得人们可能感觉开源软件曾经巩固和成熟了。但咱们进入这个扭转人类文明的致力才只有几十年,开源软件太棒了。 8. 性能优化。接下来让咱们回到高空,即时过了20年,我仍然酷爱解决性能问题。最近我的少数工作的内容都是主观和奥妙的。例如职改革, 可能须要几个月能力看到你致力的胜利。然而解决性能问题是主观的和具体的。比方这个页面加载太慢了,修复它。通常来说,修复它很简略,比方加上索引或者缓存。但有些状况,就须要深入研究,使用你多年来积攒的常识。这可能也是一个机会让你去理解磁盘如何存取数据的深奥常识,或者一些乏味的货色。有些状况,问题可能从无奈简略的被解决。所以你须要退后一步,重新考虑用户体验和基础架构。比方,你能够让用户感觉上快一点。在上班前,通常就有一个明确的解决方案。之前这个也面在最大负载场景下须要6秒。当初只须要300毫秒。哇哦,能够开香槟了。 9. Removing Toil(自动化流程)下面的点次要来自于我本身,毕竟是我列出来的依然让我喜爱编程的十件事,然而真正让我对编程这个职业痴迷不已却并不是趣味,而是它如何影响四周的世界。有些时候,我意识到我能够应用软件开发的形式来改善同胞的生存。这就是我决定这就是我余生值得谋求的货色。软件行业的衰亡对社会有着深远的影响,或好,或坏。但有一件事,我感觉齐全的好,就是打消反复的劳动。打消那些反复的,不须要用到创造力的工作。设想一下这样一个工作,理分明库存和规格之间的关系,突出缺口。这是须要一个人每天破费数小时进行文本匹配的乏味工作。这也是计算机善于的工作。当初将这项工作交给计算机,这个人就能够将工夫花在依据计算机的输入做出判断,而后与其余局部协调,确定优先级等等。 自动化也有负面影响,然而总体来说瑕不掩瑜,我置信不论任何背景的进行职业流动,并将本人独特的见解和能力带入这些职业流动时,这才是一个良好的社会。人们节约生命在一些反复艰辛的工作上不利于文化提高。 10. 写给我本人最初以我的初衷来完结探讨,最后我学习编程是因为我想要解决一些问题,我想让一些BBS的操作自动化,因为我厌倦了下载实现的时候点击下一个,下载的失败的时候点击重试。我想肯定有更好办法。过后曾经有了一种脚本语言,我记得是perl。我曾经遗记了具体的细节。但这正是这种感觉让我喜爱上了编程。你会有这样的感觉,会有更好的解决方案,因为你有编程的能力,如果你想扭转它,那通常就有更好的解决方案。 我当初曾经很少有一天去写代码了,然而我常常依然会我本人写代码。在上周我写了一个脚本每天去拉取一种鲜为人知的举重器材的价格,并将其存储到一个CSV文件中。我想取得这个价趋势,然而制造商的网站太小了无奈被大型的数据追踪器笼罩到。但这无奈难倒我,我是一个程序员,我会解决他。 论断这并不一定是编程中最让我喜爱的十件事。但它们是我脑袋中第一批想到的事。当我写作的时候,我想到了其余十几个。如果你是一名程序员,我可能漏掉了你喜爱的事。软件开发社区变的更加多样化,对于不同背景的人更加敌对。其中最酷的事件在于看到不同的人在编程中失去共鸣,他们会带来新的视角。如果有其余喜爱的事,或者不喜爱我喜爱的事请请告知,感激浏览。

January 29, 2023 · 1 min · jiezi

关于翻译:译-你的工作有趣吗

原文链接为:https://www.simplethread.com/...我在面试候选人的时候,习惯性会有短缺的工夫让候选人去问对于Simple Thread(美国弗吉尼亚的一家软件公司)的一些问题, 比方咱们的工作形式,所有在职位形容中没有列出的货色。 最近一个候选人向我问道: "你们都有哪些项目? 你们解决的是乏味的问题吗?" 这是一个十分正当的问题(It's a perfectly reasonable question), 总体上来说,这是一场体验良好的面试。全程很周到,交换也有来有往。然而对于这个问题,在过后,我却有点纳闷,我不晓得该怎么答复。 我难堪的缄默了一段时间,而后说:“哈哈哈哈,当然,我认为是这样的,我认为简直所有的问题都很乏味” 当我再次谈话的时候,我曾经复原了我的理智并且探讨了咱们的典型我的项目,突出了一些其中听起来很酷和乏味的。但这让我也开始思考,咱们是否在做乏味的我的项目? 什么是乏味的我的项目?让我过后临时困惑的起因之一是, 在不同的人对乏味的了解的不同(或趣味在于看客) ,所以有人问我是否解决了乏味的问题时,我开始思考提问者会认为什么是乏味的? 所以不同的提问者向我发问,失去的答案会有所不同。软件开发者、设计师、潜在客户、来自学校的敌人,在什么是乏味的这个问题上会有不同的认识。 只管认识不同,但也具备共性: 以后技术范畴内的极限问题, 可能须要新的技术能力解决,或者也可能无奈解决。工作于以后技术的热门畛域(机器学习,量子计算等等)解决一些重要的问题(衰弱、教育,生态)高难度的问题(超高程度的视觉设计)根本是就是一些,当你思考,你会想"哇,他们是怎么做到的" 什么是无趣的我的项目然而我想到了我做过的那些不合乎下面要求的我的项目,难道他们是无趣的吗? 对我来说是乏味的。大多数问题是有约束条件的,在约束条件尽可能的找出最优解是乏味的。可能是因为我在成为软件开发之前,我从事过批发和餐饮服务工作。兴许是我从我的家庭中听到了太多对于挣扎的故事。然而不论是什么起因,即便我曾经在这个行业数十年当前,我依然感觉十分有幸可能从事应用本人的大脑来解决逻辑问题的职业。 每个我的项目都会有因为一些限度或束缚引发一些问题的时刻,这是你的团队须要解决的一个小难题。兴许你能够在限度或束缚范畴内设计出完满的解决方案。兴许你须要重塑这个问题,使得这个问题在约束条件下能够被解决。兴许你能够回溯并从新设计自身的限度。这就是代码和数字设计中的粗浅乐趣,他们靠近于有限灵便的建筑材料。 我经验过很多人可能感觉无聊的我的项目。优化我的项目、技术债权偿还我的项目,救济我的项目,重构我的项目。然而,如果有正确的心态,解决这些问题就会显得乏味起来。 哪些项目无聊?让我持续思考,我做过的哪些项目是让我感觉无聊的,如果我的答案是没有,那么感觉我在这个问题上的答案是不可信的。 首先我认为这和束缚相干,解决一些小的问题是趣味的起源。然而,也不齐全是,我做过一些简直无限度的我的项目,绝对于须要解决的问题来说,咱们的估算相当短缺,同时也没有技术难题须要克服,工期也是正当的。问题也好了解,我的项目也是工作,交付代码、接管反馈、迭代,批改。 所以这些我的项目为什么不无聊呢? 我同你讲,这些问题也困扰着,一整天都在我的脑海外面拉扯。那天早晨,我在试图入睡的时候,我的思路又再次回到了这个问题。 这些我的项目我认为乏味的起因可能一部分在于团队十分棒,技术栈也是适合的,工作环境也十分棒。这些我的项目对咱们的用户来说也十分重要。等等,最初一个点是什么? 这个我的项目十分重要 啊哈,我想到这的这点,我一天思考解决的问题,对一些人十分重要。(原文为: Aha! As soon as I had that thought, I knew that was the idea my brain had been circling all day. The problems we were solving mattered to somebody. 啊哈!一旦我有了这个想法,我就晓得那是我的大脑一整天在思考的想法了。咱们要解决的问题对某人很重要。) 重要的问题但下面的理由并不总是充沛,在我没有看到相关性的状况下。如果我可能明确咱们构建的零碎为什么存在,为什么咱们正在解决的问题能够改善世界上的人的生存,为什么所有人都关怀这个问题 ? 那我就能从其中找到趣味。 从这个角度,来回顾我的职业生涯的话,我也加入过一些无趣的我的项目。我的项目的外围用户并没有参加,没有业务专家。没有人真正关怀它。如果没有人真的关怀正在解决的问题,那么这个我的项目就是无趣的。 在大中型企业中,这样的景象是常见的,外围用户应用这个零碎来自于领导的要求,实际上来说外围用户并不想用这个零碎。解决的也是用户并不关怀的问题。有时,你和某个商业单位的负责人单干,但其实负责人并不关怀这部分业务,他们只是在这里破费工夫,解决问题是因为被他人告知来解决它。但他们并不理解理论状况而且也不关怀。 这些相似的问题也呈现在一些初创企业上,比方,如果它们的产品和市场还没有很好的匹配或者他们并不真正置信风投的推动方向。你能够感触到他们在挣扎,无奈确定问题和解决方案。他们关怀问题,然而无奈确定解决方案是否真的无效。 是的,咱们在解决的是乏味的问题当我回顾这些年来在Simple Thread的所有我的项目时,我想不出哪个我的项目是没有意义的。咱们的许多我的项目是客户业务的外围软件,或者帮忙用户从新构建一个团队的规范工作流程,或者是为他们的次要产品提供SAAS平台。这些我的项目对咱们的客户十分重要。 ...

January 15, 2023 · 1 min · jiezi

关于翻译:我常用的两个翻译神器程序员必备-JavaGuide

你好,我是 Guide。 我从五月中旬开始,每天强制本人浏览至多一篇纯英文的文章。其实,这也算是跳出舒服区了。毕竟,雷同内容的文章,中文看一篇须要 5 分钟的话,纯英文可能须要 10~15 分钟。 我平时通常会从 Medium 这个网站上找文章浏览,我没有开明这个网站上的会员,都是找的一些收费的文章浏览,总体感觉这个网站下面的文章中等偏上,并没有很多博主吹得那么高质量,弱智低质量的文章仍然也有很多。 不过,据说在这个网站上写文章能够赚钱。之前网上意识的一位敌人,把本人国内平台的文章搬运过去赚了几百美金。 想要写博客然而you 英语比拟差且想要进步英文浏览能力的,能够通过翻译工具(如谷歌翻译、腾讯翻译)等工具将英文按段翻译,而后再用本人的话润色一下。将翻译之后的内容放在每一段英文之后,没事就拿进去浏览,长此以往,浏览的英文文章多了之后,英文浏览能力天然就上来了! 另外, 我常常应用的是彩云小译这个浏览器插件,真心好用,就是收费额度太少了! 彩云小译这个浏览器插件也能够对文章进行在线翻译,且每一段中文位于英文之后。 并且,这个工具还反对 PDF 和视频翻译,这点很实用! 这不是广告哈!我发现每次举荐工具总有个别人说是广告,恰烂钱。。。我是真无语。。。 彩云小译这工具我用下来的确能够,而且翻译品质挺高。毛病也很显著,收费额度太少了,重度应用的话,只能开会员。不过,会员的价格也不贵,一般 VIP 一年也就 98 元。 另外,沙拉查词这个划词翻译插件也很不错,评估很高。试用了几天沙拉查词之后,我把之前用的 Mate Translate 这个相似的插件给干掉了。 成果如下,这个插件反对多平台的翻译 api ,能够自行在后盾设置。 还有很多其余的性能比方单词记录、情景模式,用的不多,这里就不做介绍了。

June 8, 2022 · 1 min · jiezi

关于翻译:翻译CJS-AMD-UMD-ESM-System-和-IIFE-分别代表什么用-Rollup-示例来说明模块格式

原文地址 What Are CJS, AMD, UMD, ESM, System, and IIFE? 古代 Javascript 我的项目须要用打包工具来将小段代码编译成库或者应用程序那种更大更简单的货色。风行的打包器有webpack、Rollup、Parcel、RequireJS 和 Browserify。它们将 JavaScript 代码转换为能够作为一个 bundle 加载的模块。一个 bundle 能够用不同的格局打包。这篇文章中,咱们将展现 CJS, AMD, UMD, ESM, System 和 IIFE 格局的打包示例。 打包工具和格局上面是一个规范的 HTML 文件,第5行引入了一个款式文件,第6行引入了一个 JS 文件: <!DOCTYPE html><html lang="en"> <head> ... <link rel="stylesheet" href="style.css" /> <script src="src/index.js"></script> </head> <body> ... </body></html>简略状况下能够将所有的 JavaScript 代码放到一个文件中。随着我的项目的增大,咱们须要将代码拆分成具备独立命名空间的独立模块。除了更好的构造,模块化还带来了封装的性能、依赖治理和复用性。这就是打包工具呈现的起因。它须要将小块的 JavaScript 代码,样式表和图片编译成更大更简单的像库或者应用程序一样的货色。打包工具是如何将打包的代码格式化为输入?有很多的抉择, 上面是 Rollup 定义的一些格局: cjs (CommonJS) — 实用于 Node 和其余打包工具(别名:commonjs)。amd (Asynchronous Module Definition,异步模块化定义) — 与 RequireJS 等模块加载工具一起应用。umd (Universal Module Definition,通用模块化定义) — amd,cjs 和 iife 蕴含在一个文件中。es — 将 bundle 保留为 ES 模块文件。实用于其余打包工具,在古代浏览器中用 <script type=module> 标签引入(别名:ems, module)。system — SystemJS 加载器的原生格局 (别名:systemjs)。iife — <script> 标签引入的自执行函数。如果你想为你的利用创立一个包,你须要用到的可能就是这种。在这篇文章中,咱们将通过示例解释这些格局。 ...

September 22, 2021 · 8 min · jiezi

关于翻译:译-Vuejs-和-Alpinejs-比较

(翻译自:https://medium.com/@wearethre...) Vue.js 是以后世界上最风行的 JavaScript 框架之一,公布于 2014 年,第三个版本行将公布(译著:翻译时曾经公布),不会有大的变动。 那么咱们为什么须要另一个 JavaScript 框架呢?为什么是 Alpine?与大多数古代 JavaScript 框架不同,应用 Alpine,,只须要简略援用,不须要编译,即可执行,全副个性都能够应用。它超级轻量级,在本文编写时候,Alpine 只有 4.3kb(v.1.9.3)。然而,对我而言,Alpine.js 最吸引我的中央是它的语法,如果你曾经熟知 Vue,那你基本上理解了 Alpine,这使其非常适合 Vue 开发人员转换过去,而无需学习头疼的语法和某些奇怪的常识。Alpine 的作者 Caleb Porzio(Laravel Livewire 的作者)使其大部分的语法与 Vue 保持一致,例如:v-for 变成了 x-for,v-show 变成了 x-show 等等等等,它也引进了一些缩写语法,如x-on,所以 x-on:click=""能够简写为 @click="",你能够在 https://github.com/alpinejs/a... 理解到全副13 个语法。 (你可能好奇为什么 Alpine 应用 x- 而不是 a-,其实这是因为 Alpine 的名字确定之前,Alpine 被称为 project-x,这是对他过来名称的致敬。) 咱们接下来看一个简略的待办事项利用。”WWWWW HHHHHYYYYY?为什么还要再做一个TODO?”(我能听到你在问……)嗯,这个利用显示了很多基本概念,所以…… 从 Vue 开始有多种形式能够应用 Vue,最简略的形式应用,是间接从 CDN引入: <script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>当然,Vue 提供了 Vue CLI,要应用 Vue CLI,你须要装置 node,你能够在此找到相干信息:https://nodejs.org,而后在终端,通过命令行进行装置: npm install -g @vue/cli在你的 Vue CLI 命令行装置结束后,你能够创立你的我的项目: ...

February 28, 2021 · 3 min · jiezi

关于翻译:翻译软件设计的哲学0-序言

People have been writing programs for electronic computers for more than 80 years, but there has been surprisingly little conversation about how to design those programs or what good programs should look like. There has been considerable discussion about software development processes such as agile development and about development tools such as debuggers, version control systems, and test coverage tools. There has also been extensive analysis of programming techniques such as object-oriented programming and functional programming, and of design patterns and algorithms. All of these discussions have been valuable, but the core problem of software design is still largely untouched. David Parnas’ classic paper “On the Criteria to be used in Decomposing Systems into Modules” appeared in 1971, but the state of the art in software design has not progressed much beyond that paper in the ensuing 45 years. ...

February 17, 2021 · 7 min · jiezi

关于翻译:翻译实用的Python编程00Setup

课程设置与概述欢送拜访本课程(Practical Python Programming)。这个页面蕴含一些对于课程设置的重要信息。 课程周期和工夫要求该课程最后是作为一个由讲师主导的,继续 3 -4 天的现场培训。要全副实现本课程,您应该起码打算用25-35小时进行学习。大部分的参与者发现,在不浏览题解代码的状况下,学习材料是相当具备挑战性的(见下文) 设置和 Python 装置您只须要装置 3.6 或者更新版本的 Python 即可。不依赖于任何特定的操作系统,编辑器,IDE,或者其它与 Python 相干的工具。没有第三方依赖。 也就是说,本课程大部分内容波及学习如何编写脚本与小型程序,这些脚本与小型程序波及从文件中读取数据。因而,您须要确保您处在一个能够轻松解决文件的环境中。这包含应用编辑器创立 Python 程序,并可能从 shell 或终端运行这些程序。 您可能偏向于应用更具交互性的环境来学习本课程,例如 Jupyter Notebooks。我倡议不要这样做。只管 Jupyter Notebooks 十分棒,但本课程中的许多练习传授与程序组织的相干的观点,包含应用函数,模块,导入语句以及重构源代码逾越多个文件的程序。以我的教训,很难在 Jupyter Notebooks 环境中反复这样的环境。 派生(Forking)/克隆(Cloning) 课程仓库为了筹备本课程的环境,我举荐您从本课程的仓库 https://github.com/dabeaz-course/practical-python 派生您本人的 GitHub 仓库。实现后,您能够将其克隆到本地计算机上: bash % git clone https://github.com/yourname/practical-pythonbash % cd practical-pythonbash %请在 practical-python/ 目录下实现所有的练习。如果将解题代码提交回派生的仓库,那么您的所有代码会保留到一个中央。实现后,您将领有良好的学习记录。 如果您不想派生一个本人的 GitHub 仓库或者您没有 GitHub 账号,您依然能够将本课程的仓库克隆到您本人的计算机上: bash % git clone https://github.com/dabeaz-course/practical-pythonbash % cd practical-pythonbash %如果这样做,除了对计算机的本地正本进行更改外,您将无奈提交代码更改到 GitHub 上。 课程排版在 Work/ 目录下实现所有的编程工作。在Work/ 目录外面,有一个 Data/ 目录。 Data/ 目录蕴含各类在课程中应用的数据文件及其它脚本。您将会常常拜访位于 Data/ 目录下的文件。课程练习假设您在 Work/ 目录下创立程序。 ...

February 1, 2021 · 1 min · jiezi

关于翻译:火山引擎在机器写作和机器翻译方面的最新进展

随着新媒体平台的衰亡,人工智能技术曾经大大提高了信息内容的创作,而个性化举荐算法的信息又为信息内容的散发提供了极大的便当,这其中,文本生成技术十分重要,因为它在很多的利用场景有宽泛的利用,比方机器翻译、机器写作、对话机器人以及主动问答。2019 年在《管理科学》杂志上 MIT 钻研人员发表的一项最新钻研表明,机器翻译技术曾经将国际化贸易量进步了 10%,这相当于将地球上的各个国家之间的间隔缩短了 25% [1]。 近年来,字节跳动也研发了多项先进的机器翻译技术,目前字节跳动自研的火山翻译平台曾经有公司内外的 50 多个客户应用,反对超过 50 多种语言的相互翻译。此外,在字节跳动咱们研发了 Xiaomingbot 主动写稿平台,自 2016 年上线以来,曾经累计写了 60 万篇文章,笼罩了 17 项的体育赛事,反对 6 种语言,在自媒体平台下面也有 15 万的粉丝。 上面给大家展现一下 Xiaomingbot 如何主动写新闻。 咱们的零碎将从数据源获取到较量信息,例如球员较量布阵、球员的进球等等信息。同时咱们还会利用计算机视觉的算法,对较量视频进行剖析辨认出其中的球员、球衣下面的号码,球员的静止轨迹、球员的动作、球员的地位以及要害的一些场景等等。再利用这些信息咱们利用文本生成算法写出最初的文章 [2]。 在另外一项钻研当中咱们应用计算机视觉的算法去剖析斯诺克较量的静止、桌上球的静止轨迹、以及利用机器学习最初去预测球员的击球策略,预测下一杆球会落到哪个袋,并且利用这些预测去生成最终的较量讲解 [3]。这对于一些非职业的观众来说,十分有助于帮忙了解球赛的过程。这是咱们算法最终生成的一些讲解状况。 本场讲座,会分为五局部内容。第一局部,我会给大家先简略介绍一下什么是序列生成问题,它有什么样的难度和挑战;第二局部,将介绍深度隐变量模型,Deep latent Variable Models for Text Generation;第三局部,我将介绍文本生成当中如果加上限度之后,如何做更好的算法,咱们提出了一类蒙特卡洛采样算法来做文本生成;第四局部会介绍机器翻译当中如何使一个模型能够去获取四项双语语言能力。最初一部分介绍多语言的机器翻译,咱们最新的一个工作 mRASP。 序列生成问题的难度和挑战在自然语言中,所有自然语言宣称的外围问题是对句子序列做建模,比如说这样一个句子的 The quick brown fox jumps over the lazy dog 句号,这里有 10 个字符,Modeling 的问题就是对这 10 个字符的联结概率去建模,也就任意一个句子长度为 L 的句子,我须要对整个 L 各字符对它算出它的联结概率分布。当然最根本的一种办法是叫 Auto-Regressive Language model,是把这个联结概率分解成上面这个模式,每一个局部它实际上是第 i 个字符的概率,是建设在后面 1 到 i-1 个字符的根底之上,这具体的每一个概率能够有很多建模的办法。比如说当初从 2017 年开始比拟风行的叫 Transformer 网络外面对个条件概率的建模是应用多层的多头注意力机制(Muti-Head Attention)来建模的 [4]。当然这个 Transformer 有很多的参数,理论学习当中就须要找到最好的一组参数,使得语料外面的联结概率最大。 ...

January 28, 2021 · 5 min · jiezi

关于翻译:2021年你需要的7个JS-Array方法

前言文本翻译至 2021年您将须要的7种JS数组办法 JavaScript 为咱们提供了大量解决数组的不同办法。咱们将在短短几分钟内为您介绍7个基本知识,以进步您的JS开发技能 注释1.Array.map()当咱们在数组上应用该 .map() 办法时,它都会在原数组根底尚返回一个新的批改版本。该 .map() 办法具备循环遍历您的数组并且批改的性能。 .map() 每当您要更新数组中的每一项并且须要返回新数组时,都能够应用该办法 假如咱们有一个蕴含汽车品牌的数组: const cars = ["Porsche", "Audi", "BMW", "Volkswagen"];当然,咱们认为所有的汽车品牌都很酷,咱们须要给每个品牌来加以表白,咱们能够应用 .map() 办法。 const coolCars = cars.map(car =>`$ {car}是一个十分酷的汽车品牌!`);//后果:[“保时捷是一个十分酷的汽车品牌!”,“奥迪是一个十分酷的汽车品牌!”,“宝马是一个十分酷的汽车品牌!”,“大众汽车是一个十分酷的汽车品牌!”]] 太棒了!该 .map() 办法创立了一个新数组并将形容文本增加到每个我的项目。 很快乐的事,咱们还晓得如果应用 .map() 办法解决蕴含对象的数组 让咱们有一堆价格不含税的汽车,而后应用加上含税价格 .map() const carsWithPrice = [ {brand: "保时捷", price: 100000}, {brand: "奥迪", price: 80000}];const carsWithPriceAndTax = cars.map(carObject => { return { // 返回原对象 ...carObject, // 返回新的含税的价格新值 priceWithTax: carObject.price * 1.2 }});// 后果:[ {brand: "保时捷", price: 100000, priceWithTax: 120000}, {brand: "奥迪", price: 80000, priceWithTax: 96000}]; ...

January 25, 2021 · 2 min · jiezi

关于翻译:javascript是如何工作的03内存管理和如何处理4种常见的内存泄漏

概述像C语言这种具备底层内存治理的原始语言,例如malloc()和free()。开发人员应用这些原始语言明确地给操作系统调配和开释内存。 同时,JavaScript 在创立事物(对象,字符串,等等)的时候分配内存,并且在不再应用的时候“主动”开释内存,这是一个垃圾回收的过程。开释资源的这种看起来“主动”的个性是凌乱的起源,给 JavaScript (和其余高级语言)开发者一种他们能够不再关怀内存治理的谬误印象。这是一个大谬误。 即便在应用高级语言工作的时候,开发者应该了解内存治理(或者起码是基本知识)。有时,主动内存治理存在问题(例如垃圾回收中的bug或者实现限度,等),开发人员必须理解这些问题能力正确处理它们(或者找到适当的解决办法,同时尽量减少老本和代码累赘)。 内存生命周期无论你应用哪种编程语言,内存生命周期简直都是一样的: 以下是对周期中的每一步产生的状况概述: 分配内存 - 内存是由操作系统调配的,操作系统容许你的程序应用它。在底层级别语言(例如C)中,这是你作为一个开发者应该解决的明确操作。然而,在高级别语言,这是你应该解决的。应用内存 - 这实际上是程序应用之前调配的内存的时候。读和写操作在代码中分配内存的时候产生。开释内存 - 当初是时候开释你不再须要的整个内存了,这样它能够从新开释并且可再次应用。与分配内存一样,在低级别语言中这是明确的。要疾速理解调用栈和内存堆的概念,能够浏览这个主题的第一篇文章。 什么是内存?在间接进入 JavaScript 内存之前,咱们将简略地探讨下什么是个别内存,以及它是如何工作的。 在硬件层面,计算机内存是由大量的触发器组成。每个触发器蕴含几个晶体管,并且能够存储一个比特。每个触发器通过一个惟一的标识符寻址,因而咱们能够读取和复写它们。因而,从概念上讲,咱们能够认为整个计算机的内存是一组比特数组,咱们能够读写他们。 然而作为人类,咱们并不善于应用比特来实现咱们所有的思考和算数,咱们将它们组成更大的组,它们能够一起来示意数字。8比特被称为1字节。除了字节,还有单词(有时是16,有时是32位)。 内存中存储大量的货色: 所有的变量和所有程序应用的数据。编程的代码,包含操作系统的。编译器和操作系统一起工作,为你解决大量的内存治理,然而咱们倡议你查看一下背地产生的事件。 当你编译你的代码的时候,编译器能够查看原始数据类型,并且提前计算它们须要多少内存。而后在调用堆栈空间中将须要的内存调配给程序。调配这些变量的空间叫做堆栈空间,因为在调用函数的时候,他们的内存会增加到现有的内存之上。一旦他们停止,它们将以LIFO(后入先出)的程序移除。例如,思考上面的申明: init n; // 4bytesint x[4]; // array of 4 elements, each 4 bytesdouble m; // 8 bytes编译器能够立马看到这些代码须要多大内存 4 + 4 * 4 + 8 = 28 bytes.这就是它如何解决以后整型和double的大小。大略20年前,整型通常是2bytes,和4字节。你的代码不应该依赖以后根本数据类型的大小。编译器将插入与操作系统交互的代码,它须要申请以后栈上所需存储变量的大小的字节数。 在下面的例子中,编译器确切地晓得每个变量的内存地址。实际上,不论你什么时候书写变量n,这将会在外部被翻译成相似于“内存地址 4127963”。 留神到,如果咱们试图在这里拜访x[4],咱们将须要拜访与之关联的m。这是因为咱们拜访了数组上不存在的元素 - 它比实际上调配给数组的最初一个元素x[3]多了4字节,并且可能将会最终读取(或者复写)一些m的比特。这将对剩下的程序产生十分不冀望的结果。 当一个函数调用另一个函数时,每个函数在调用堆栈时都会失去本人的堆栈块。它将其所有的本地变量都保留在这里,并且有一个程序计数器去记住执行过程中的地位。当这个函数调用实现,它的内存块将会用于其余用处。 动态分配可怜的是,当咱们不晓得一个变量在编译的时候须要多少内存,事件就变得不简略了,如果咱们要做上面的事件: int n = readInput(); // reads input from the user...// create an array with "n" elements这里,在编译的时候,编译器不晓得这个数组须要读书内存,因为它是由user提供的值决定的。 ...

October 30, 2020 · 4 min · jiezi

关于翻译:javascript是如何工作的02V8引擎内部机制及如何编写优化代码的5个诀窍

概述一个 JavaScript 引擎是一个执行 JavaScript 代码的程序或者解释器。一个JavaScript 引擎能够实现成一个规范的解释器,也能够是一个实时编译器,它以某种像是将 JavaScript 编译为字节码。 这是一个正在实现 JavaScript 引擎的热门我的项目列表: V8) — 开源,谷歌开发,应用C++编写Rhino - Wikipedia](https://en.wikipedia.org/wiki... — 由Mozilla基金会治理,开源,齐全应用Java开发SpiderMonkey) — 第一个JavaScript引擎,在过后反对 Netscape Navigator, 明天反对FirefoxJavaScriptCore -- 开源,作为Nitro销售,由苹果公司为Safari开发KJS - Wikipedia](https://en.wikipedia.org/wiki... — KDE 引擎起初由 Harri Porten 为KDE Konqueror web 浏览器我的项目而开发Chakra (JScript9) - Wikipedia](https://en.wikipedia.org/wiki... — IE浏览器Chakra (JavaScript) - Wikipedia](https://en.wikipedia.org/wiki... — 微软 EdgeNashorn - Wikipedia](https://en.wikipedia.org/wiki... —作为JDK的一部分开源,由Oracle的Java语言和工具组编写 JerryScript) — 物联网的轻量级引擎为什么要创立V8引擎?V8引擎是由谷歌应用C++编写创立的开源引擎。这个引擎被用于谷歌的Chrome外部。然而,与其余引擎不同的是,V8页用于风行的Node.js运行时。 V8是第一个为了进步 JavaScript 在 web 浏览器的执行的性能优化而设计的。为了进步速度,V8将 JavaScript 代码转换为效率更改的机器代码,而不是应用外部解释器。它通过实现JIT(Just-In-Time) compiler 将 JavaScript 代码在运行的时候转为机器代码,就像许多古代的JavaScript引起做的一样,例如 SpiderMonkey 或者 Rhino(Mozilla)。这里的次要区别是V8不产生字节码或者任何其余中间代码。 ...

October 30, 2020 · 2 min · jiezi

关于翻译:javascript是如何工作的引擎运行时和调用栈的概述

随着 JavaScript 的越来越风行,促使团队在多个栈上都须要它反对 - 前端、后端、混合应用程序、嵌入式设施等等。 这篇文章是这个系列的第一篇,目标是深入研究 JavaScript 以及它真正是如何工作的:咱们认为理解 JavaScript 的构建块,并且晓得它是如何一起运行的,那么你将会写出更好的代码和应用程序。咱们也将分享一些咱们在应用构建 SessionStatck时候的一些教训规定,一个轻量的应用程序,为了放弃竞争力,必须是强壮和高性能的。 正如GitHut statsGitHut - Programming Languages and GitHub统计数据展现, JavaScript 是 GitHub 上 Repositories 最沉闷和 push 最多的语言。在其余的类别中,它也没有落后。 获取最新的GitHub language stats) 如果我的项目越来越多的依赖 JavaScript,这意味着开发者为了构建一个令人惊叹的软件,必须利用语言提供的所有,并且对于生态系统外部有着深刻的了解。 事实证明, 很多开发者每天都在应用 JavaScript,然而并不知道它们外部产生了什么。 概述简直每个人都曾经据说过V8引擎的概念,并且很多人晓得 JavaScript 是单线程的或者它是应用回调队列的。 在这篇文章中,咱们将会具体讲述所有的概念,并且解释 JavaScript 是如何真正运行的。在晓得这些字节之后,你将会适当的利用提供的APIS写出更好的,非阻塞的应用程序。 如果你是 JavaScript 老手,这篇文章会帮忙你了解为什么 JavaScript 和其余语言比拟起来如此“怪异”。 如果你是一个有教训的 JavaScript 开发者,心愿它能够让你对每天都在应用的 JavaScript 运行时是如何真正关注的有一些新的了解。 JavaScript 引擎最风行的 JavaScript 引擎的例子是谷歌的 V8 引擎。V8被用于 Chrome 和 Node.js 外部。上面是一个简略的视图示例: 引擎次要由两个局部组成: 内存堆 — 这是内存调配的中央调用栈 — 这是代码执行的栈运行时有很多浏览器的APIS被 JavaScript 开发者应用过(例如:setTimeout)。然而这些APIS,并不是引擎提供的。 ...

October 30, 2020 · 1 min · jiezi

关于翻译:介绍在使用-Solidity-以太坊升级智能合约的挑战

在开发软件的时候,咱们常常须要公布新的版本来减少新的性能或者修复bug。当波及到智能合约开发时,也没有什么区别。尽管将智能合约更新到新版本通常不像更新其余类型的雷同复杂性的软件那么简略。 大多数区块链,尤其是像 Ethereum 这样的公链,都实现了不可变的个性。实践上不容许任何人扭转区块链的 "过来"。不可变更性实用于区块链中的所有交易,包含用于部署智能合约和相干代码的交易。换句话说,一旦智能合约的代码被部署到区块链上,它将永远 "原样 "地 "活着"--没有人能够扭转它。如果发现了bug或者须要增加新的性能,咱们无奈间接批改部署合约的代码。 如果一个智能合约是不可更改的,那么你如何可能将它降级到新的版本?答案就在于将新的智能合约部署到区块链上。但这种办法会带来一些须要解决的挑战。最根本也是最常见的是,所有应用智能合约的用户都须要参考新合约版本的地址,第一个合同的版本应该被禁用,强制每个用户应用新版本。 通常,你须要确保旧版本的数据(状态)被迁徙或以某种形式提供给新版本。在最简略的状况下,这意味着你须要将旧版本中的状态复制/迁徙到新合同的版本中。 上面的章节将更具体地形容这些挑战。为了更好的阐明,咱们用上面两个版本的 MySmartContract 作为参考。 // Version 1contract MySmartContract { uint32 public counter; constructor() public { counter = 0; } function incrementCounter() public { counter += 2; // This "bug" is intentional. }}// Version 2contract MySmartContract { uint32 public counter; constructor(uint32 _counter) public { counter = _counter; } function incrementCounter() public { counter++; }}用户可参考新合同的地址。当部署到区块链时,智能合约的每个实例都被调配到一个惟一的地址。该地址用于援用智能合约的实例,以便调用其办法并从/向合约的存储(状态)读取/写入数据。当你将合同的更新版本部署到区块链时,合同的新实例将部署在一个新的地址。这个新地址与第一个合约的地址不同。这意味着,所有与智能合约交互的用户、其余智能合约和/或 dApp(去中心化利用)都须要更新,以便它们应用更新版本的地址。剧透:有一些选项能够防止这个问题,你会在本节最初看到。 那么,让咱们思考以下状况。你用下面 Version 1 的代码创立 MySmartContract。它被部署到区块链的地址 A1(这不是一个实在的 Ethereum 地址--仅用于阐明目标)。所有想要与 Version 1交互的用户都须要应用地址 A1 来援用它。当初,通过一段时间后,咱们留神到了办法 incrementCounter 中的 bug:它是以2来递增计数器,而不是以1来递增它,所以咱们实现了一个修复,产生了 MySmartContract 的 Version 2 版本。这个新合约的版本被部署到地址 D5 的区块链上。此时,如果用户想要与 Version 2 进行交互,须要应用地址 D5,而不是 A1。这就是为什么所有与 MySmartContract 交互的用户都须要更新,以便他们参考新的地址 D5 的起因。 ...

September 13, 2020 · 4 min · jiezi

关于翻译:golang的逃逸分析

翻译自:http://www.agardner.me/golang/garbage/collection/gc/escape/analysis/2015/10/18/go-escape-analysis.html Golang逃逸剖析2015-10-18 垃圾回收是Go的一个很便当的个性--其主动的内存治理使代码更整洁,同时缩小内存透露的可能性。然而,因为垃圾回收须要周期性的进行程序从而去收集不必的对象,不可避免的会减少额定开销。Go编译器是智能的,它会主动决定一个变量是应该调配在堆上从而在未来便于回收,还是间接调配到函数的栈空间。对于调配到栈上的变量,其与调配到堆上的变量不同之处在于:随着函数的返回,栈空间会被销毁,从而栈上的变量被间接销毁,不须要额定的垃圾回收开销。 Go的逃逸剖析绝对于Java虚拟机的HotSpot来说更为根底。根本规定就是,如果一个变量的援用从申明它的函数中返回了,则产生“逃逸”,因为它有可能在函数外被别的内容应用,所以必须调配到堆上。如下几种状况会比较复杂: 函数调用其余函数援用作为构造体的成员变量切片和映射Cgo指向变量的指针为了实现逃逸剖析,Go会在编译阶段结构函数调用关系图,同时跟踪入参和返回值的流程。 ---待持续

August 12, 2020 · 1 min · jiezi

翻译We-have-a-problem-with-promises

原文:https://pouchdb.com/2015/05/1... JavaScripts的朋友们,是时候承认了: we have a problem with promises。不,不是promises本身。正如A+ spec所定义的,promises是非常棒的。 在过去的一年里,当我看到许多程序员在PouchDB API和其他promise-heavy APIs上挣扎时,我发现了一个大问题:我们中的许多人使用promises 时没有真正理解它们。 如果你觉得很难相信,想想我最近在Twitter上发布的这个谜题: Q: What is the difference between these four promises? doSomething().then(function () { return doSomethingElse();});doSomething().then(function () { doSomethingElse();});doSomething().then(doSomethingElse());doSomething().then(doSomethingElse);如果你知道答案,那么恭喜你:你是一个承诺忍者。我允许您停止阅读此日志。对于其他99.99%的人来说,你是一个很好的同伴。没有人回应我的推特,也没有人能解决这个问题,我自己对#3的答案感到惊讶。是的,即使我写了测验!答案在这篇文章的最后,但首先,我想探讨一下为什么promises一开始就那么棘手,为什么我们中的许多人——新手和专家——会被promises绊倒。我还将提供我认为是独特见解的东西,一个奇异的把戏,它使promises很容易理解。是的,我真的相信在那之后他们不会那么难!但首先,让我们挑战一些关于promises的常见假设。Wherefore promises?如果你读过有关promises的文献,你会经常发现对the pyramid of doom(https://medium.com/@wavded/managing-node-js-callback-hell-1fe03ba8baf)的引用,其中有一些可怕的callback-y代码稳步地向屏幕的右侧延伸。promises确实解决了这个问题,但它不仅仅是缩进。正如"Redemption from Callback Hell"(http://youtu.be/hf1T_AONQJU)中所解释的,callbacks的真正问题是它们剥夺了我们return和throw这样的关键字。相反,我们的程序的整个流程基于side effects:一个函数偶然调用另一个函数。事实上,callbacks 做了一些更险恶的事情:它们剥夺了我们的stack, stack在编程语言中我们通常认为是理所当然的。写没有stack的代码很像驾驶一辆没有刹车踏板的汽车:你不会意识到你有多么需要它,直到你伸手去拿它而它不在那里。promises的全部要点是就是把异步时丢失的语言基础还给我们:return, throw, 和 stack。但是你必须知道如何正确地使用promises,才能利用它们。Rookie mistakes有些人试图把承诺解释成cartoon(https://www.andyshora.com/promises-angularjs-explained-as-cartoon.html),或者以一种非常面向名词的方式:“哦,正是你可以传递的东西代表了一个异步值。”我觉得这样的解释没什么帮助。对我来说,promises都是关于代码结构和流程的。所以我认为最好是回顾一些常见的错误,并展示如何修复它们。我把这些叫做"rookie mistakes",意思是,“你现在是新手了,孩子,但你很快就会成为职业选手。”Quick digression::“promises”对不同的人来说意味着很多不同的事情,但是在本文中,我将只讨论官方规范(https://promisesaplus.com/),就像window.Promise在现代浏览器中一样。并不是所有的浏览器都有window.Promise,因此,要想得到一个好的polyfill,请看一个名为Lie(https://github.com/calvinmetcalf/lie)的库,它是目前最小的符合规范的库。Rookie mistake #1: the promisey pyramid of doom看看人们是如何使用PouchDB的,PouchDB有一个很大程度上基于promise的API,我发现很多糟糕的promise模式。最常见的糟糕的做法是:remotedb.allDocs({ include_docs: true, attachments: true}).then(function (result) { var docs = result.rows; docs.forEach(function(element) { localdb.put(element.doc).then(function(response) { alert("Pulled doc with id " + element.doc._id + " and added to local db."); }).catch(function (err) { if (err.name == 'conflict') { localdb.get(element.doc._id).then(function (resp) { localdb.remove(resp._id, resp._rev).then(function (resp) {// et cetera...是的,事实证明你可以像回调一样使用promises ,是的,这很像用电动砂光机锉指甲,但你可以做到。如果你认为这类错误仅仅局限于绝对初学者,你会惊讶地发现我确实从官方的黑莓开发者博客中获取了上述代码!旧的回调习惯很难改变。(对开发人员说:很抱歉挑你的毛病,但你的例子很有启发性。)A better style is this one:remotedb.allDocs(...).then(function (resultOfAllDocs) { return localdb.put(...);}).then(function (resultOfPut) { return localdb.get(...);}).then(function (resultOfGet) { return localdb.put(...);}).catch(function (err) { console.log(err);});这被称为composing promises,它是promises的great superpowers之一。每个函数只有在上一个Promise resolved后才会被调用,并且将使用该Promise的输出来调用它。更多的内容以后再谈。Rookie mistake #2: WTF, how do I use forEach() with promises?这就是大多数人对承诺的理解开始崩溃的地方。一旦他们到了熟悉的foreach()循环(或者for循环,或者while循环),他们就不知道如何让它与promises一起工作。所以他们写了这样的东西: // I want to remove() all docs db.allDocs({include_docs: true}).then(function (result) { result.rows.forEach(function (row) { db.remove(row.doc); }); }).then(function () { // I naively believe all docs have been removed() now! });这个代码有什么问题?问题是第一个函数实际上返回undefined,这意味着第二个函数不等待对所有文档调用db.remove()。实际上,它不需要等待任何东西,并且可以在删除任意数量的文档后执行!这是一个特别阴险的bug,因为您可能不会注意到任何错误,假设PouchDB删除这些文档的速度足以更新您的UI。这个bug可能只在odd race条件下出现,或者在某些浏览器中出现,此时几乎不可能进行调试。所有这些的TLDR 都是forEach()/for/while 不是您要查找的构造。你需要Promise.all(): db.allDocs({include_docs: true}).then(function (result) { return Promise.all(result.rows.map(function (row) { return db.remove(row.doc); })); }).then(function (arrayOfResults) { // All docs have really been removed() now! });这是怎么回事?基本上Promise.all() 接受一个array of promises作为输入,然后它给您另一个promise,该promise只在其他所有的promise都resolved时才会解决。它是for循环的异步等价物。Promise.all() 还将一个结果数组传递给下一个函数,这非常有用,例如,如果您试图从pouchdb去get()多个结果。如果它的任何一个sub-promises are rejected,那么all()承诺也会被拒绝,这更有用。Rookie mistake #3: forgetting to add .catch()这是另一个常见的错误。幸运的是,他们的promises永远不会抛出错误,许多开发人员忘记在代码中的所有地方添加.catch()。不幸的是,这意味着任何抛出的错误都将被吞没,您甚至不会在控制台中看到它们。这可能是调试真正的苦恼。为了避免这种糟糕的情况,我养成了在我的promise chains中添加以下代码的习惯: somePromise().then(function () { return anotherPromise(); }).then(function () { return yetAnotherPromise(); }).catch(console.log.bind(console)); // <-- this is badass即使您不期望出现错误,也要谨慎地添加catch()。如果你的假设被证明是错误的,这会让你的生活更轻松。Rookie mistake #4: using "deferred"这是一个错误 我看all the time,我甚至不愿意在这里重复它,因为我担心,像甲虫汁一样,仅仅调用它的名字就会引发更多的例子。简言之,promises 有着悠久的历史,而JavaScript社区花了很长时间才使其正确。早期,jQuery 和Angular在各地都使用这种“deferred”模式,现在已经被ES6 Promise规范所取代,由“good”库(如Q, When, RSVP, Bluebird, Lie, and others库)实现。所以如果你在代码中写这个词(我不会第三次重复!)你做错了一些事。下面是如何避免它。首先,大多数承诺库都为您提供了从第三方库“import”promises 的方法。例如,Angular的$q模块允许您使用$q.when()包装non-$q承诺。所以Angular用户可以这样包装PouchDB承诺: $q.when(db.put(doc)).then(/* ... */); // <-- this is all the code you need另一种策略是使用revealing constructor pattern(https://blog.domenic.me/the-revealing-constructor-pattern/),这对于包装 non-promise的API很有用。例如,要包装基于回调的API,如Node的fs.readfile(),只需执行以下操作: new Promise(function (resolve, reject) { fs.readFile('myfile.txt', function (err, file) { if (err) { return reject(err); } resolve(file); }); }).then(/* ... */)Done! We have defeated the dreaded def... Aha, caught myself. :)有关为什么这是anti-pattern的更多信息,请访问Bluebird wiki上的Promise anti-patterns页面(https://github.com/petkaantonov/bluebird/wiki/Promise-anti-patterns#the-deferred-anti-pattern)。Rookie mistake #5: using side effects instead of returning这个代码怎么了? somePromise().then(function () { someOtherPromise(); }).then(function () { // Gee, I hope someOtherPromise() has resolved! // Spoiler alert: it hasn't. });好吧,这是一个很好的观点,可以谈论关于promises的所有你需要知道的事情。说真的,这是一个one weird trick,一旦你理解了它,就会阻止我所说的所有错误。准备好了吗?正如我之前所说,promises 的魔力在于,它们把我们宝贵的return 和 throw还给我们。但在实践中这到底是什么样子的呢?每一个承诺都会给你一个then()方法(或catch(),它只是then(null, ...)的语法糖)。这里是then()函数的内部: somePromise().then(function () { // I'm inside a then() function! });我们在这里能做什么?有三件事: 1. return another promise 2. return a synchronous value (or undefined) 3. throw a synchronous error就这样。一旦你理解了这个诀窍,你就明白了promises。所以,So let's go through each point one at a time.。1. Return another promise 这是您在promise文献中看到的常见模式,如上面的“composing promises”示例所示: getUserByName('nolan').then(function (user) { return getUserAccountById(user.id); }).then(function (userAccount) { // I got a user account! }); 请注意,我正在返回第二个promise—return是至关重要的。如果我没有说return,那么getUserAccountByID()实际上是一个side effect,下一个函数将接收undefined而不是userAccount。2. Return a synchronous value (or undefined)返回undefined通常是一个错误,但返回同步值实际上是将同步代码转换为Promisey代码的一种很棒的方法。例如,假设我们有一个用户的内存缓存。我们可以做到: getUserByName('nolan').then(function (user) { if (inMemoryCache[user.id]) { return inMemoryCache[user.id]; // returning a synchronous value! } return getUserAccountById(user.id); // returning a promise! }).then(function (userAccount) { // I got a user account! });那不是太棒了吗?第二个函数不关心是同步还是异步获取用户帐户,第一个函数可以自由返回同步或异步值。不幸的是,在JavaScript中,non-returning函数在技术上返回undefined结果是不方便的,这意味着当您打算返回某些内容时,很容易意外地引入side effects 。出于这个原因,我习惯于总是从then()函数内部返回或抛出。我建议你也这么做。Throw a synchronous error说到throw,这就是promises可以变得令人惊叹的地方。假设我们想要抛出一个同步错误,以防用户注销。这很容易: ...

July 6, 2019 · 5 min · jiezi

Numba-044-中文文档校对活动-ApacheCN

整体进度:https://github.com/apachecn/n... 贡献指南:https://github.com/apachecn/n... 项目仓库:https://github.com/apachecn/n... 贡献指南请您勇敢地去翻译和改进翻译。虽然我们追求卓越,但我们并不要求您做到十全十美,因此请不要担心因为翻译上犯错——在大部分情况下,我们的服务器已经记录所有的翻译,因此您不必担心会因为您的失误遭到无法挽回的破坏。(改编自维基百科)负责人: 飞龙:562826179章节列表1. 用户手册 1.1。 Numba 的约 5 分钟指南1.2。概述1.3。安装1.4。使用@jit 编译 Python 代码1.5。使用@generated_jit 进行灵活的专业化1.6。创建 Numpy 通用函数1.7。用@jitclass 编译 python 类1.8。使用@cfunc 创建 C 回调1.9。提前编译代码1.10。使用@jit 自动并行化1.11。使用@stencil装饰器1.12。从 JIT 代码 中回调到 Python 解释器1.13。性能提示1.14。线程层1.15。故障排除和提示1.16。常见问题1.17。示例1.18。会谈和教程2. 参考手册 2.1。类型和签名2.2。即时编译2.3。提前编译2.4。公用事业2.5。环境变量2.6。支持的 Python 功能2.7。支持的 NumPy 功能2.8。与 Python 语义的偏差2.9。浮点陷阱2.10。 Python 2.7 寿命终止计划3. 用于 CUDA GPU 的 Numba 3.1。概述3.2。编写 CUDA 内核3.3。内存管理3.4。编写设备功能3.5。 CUDA Python 中支持的 Python 功能3.6。支持的原子操作3.7。随机数生成3.8。设备管理3.10。示例3.11。使用 CUDA 模拟器 调试 CUDA Python3.12。 GPU 减少3.13。 CUDA Ufuncs 和广义 Ufuncs3.14。共享 CUDA 内存3.15。 CUDA 阵列接口3.16。 CUDA 常见问题4. CUDA Python 参考 ...

July 6, 2019 · 1 min · jiezi

翻译逐步替换Sass

本文来自心谭博客·「译文」逐步替换Sass,最新文章请见导航页,欢迎交流✿✿ヽ(°▽°)ノ✿翻译说明这是一篇介绍现代 css 核心特性的文章,并且借助 sass 进行横向对比,充分体现了 css 作为一门设计语言的快速发展以及新特性为我们开发者带来的强大生产力。 第一次尝试翻译技术文,为了让文章更通俗易懂,很多地方结合了文章本意和自己的说话风格。另外,时间有限水平有限,难免有些失误或者翻译不恰当的地方,欢迎指出讨论。 英文原文地址:https://cathydutton.co.uk/posts/why-i-stopped-using-sass/ 正文开始我每年都要重新搭建和设计我的网站,这是一个非常不错的方式去跟进 HTML/CSS 的最新进展、开发模式和网站生成器。在上个月,我发布了新版本:从 Jekyll 和 GithubPages 迁移到 Eleventy 和 Netlify。 一开始,我并没有移除代码中所有的 sass 代码。这本不是我计划中的事情,但随着我不断查看 sass 代码,我一直在思考:它们是否给网站带来了价值,还是仅仅增加了复杂度和依赖性(特指对:scss)?随着这年 css 的发展,曾经让我使用 sass 的原因似乎不那么重要了。 其中一个例子就是我已经移除了媒体查询。当我了解到 CSS 的一些新的特性,那些针对特定屏幕大小的代码(媒体查询)没有必要,因此被移除了。 Sass 解决了什么问题?大概 5、6 年前,我第一次了解到 sass 的时候,我是有些换衣的。随着我搭建越来越多的响应式 web 应用,我才意识到借助 sass 的  functions  和  mixins  可以大大提高代码复用。显而易见的是,随着设备、视图窗口和主题等场景的变化,使用(sass 的)变量让代码迁移的成本更低。 下面是我用 sass 做的事情: 布局变量Typography1) 布局布局一直是 css 中让人困惑的地方。而响应式布局正是我最初决定使用 Sass 去创建 css 布局的重要原因。 使用 sass我一直记得我第一次尝试用 css 创建一个响应式网格布局的时候,那要为每列创建一个对应的类名,然后再用语义化不强的类名(比如  col-span-1  和  col-span-4 )来标记它。 .col-span-3 { float: left; width: 24%; margin-left: 1%;}.col-span-4 { float: left; width: 32.3%; margin-left: 1%;}.col-span-5 { float: left; width: 40.6%; margin-left: 1%;}借助 sass 的  mixin  和变量,能够不再编写像上面那样的类名。并且能够通过改变  $gridColumns  变量,来创造更灵活的布局。 ...

June 19, 2019 · 2 min · jiezi

ApacheCN-翻译活动进度公告-2019615

Special Sponsors 我们组织了一个开源互助平台,方便开源组织和大 V 互相认识,互相帮助,整合资源。请回复这个帖子并注明组织/个人信息来申请加入。请回复这个帖子来推荐希望翻译的内容。如果大家遇到了做得不错的教程或翻译项目,也可以推荐给我们。我们会联系项目的维护者,一起把它变得更好。我们的公众号接受大家的投稿。将文章链接通过消息发给公众号,我们会和你联系。为了能够将开源事业做大做强,ApacheCN 需要与公益基金会(IT、教育类)合作,欢迎大家提供帮助。如果你不希望再收到我们的邮件,请直接拉黑我们,不要浪费彼此的时间,谢谢合作。如果大家有适合我们的增长策略,包括你想尝试但是怕被骂的策略,可以交给我们来做实验。我们将使用激进的增长策略,尽可能让更多人了解我们。CS224n 自然语言处理(笔记整理)参与方式:https://github.com/apachecn/s... 整体进度:https://github.com/apachecn/s... 项目仓库:https://github.com/apachecn/s... 认领:11/20,整理:0/20 章节贡献者进度Lecture 1@cx123cx456 Lecture 2@AllenZYJ Lecture 3@cx123cx456 Lecture 4@ZSIRS Lecture 5@ZSIRS Lecture 6@ZSIRS Lecture 7@neolei Lecture 8 Lecture 9@NewDreamstyle192 Lecture 10@enningxie Lecture 11 Lecture 12 Lecture 13 Lecture 14 Lecture 15 Lecture 16 Lecture 17@pingjing233 Lecture 18 Lecture 19 Lecture 20@Willianan 短篇集(校对)参与方式:https://github.com/apachecn/m... 整体进度:https://github.com/apachecn/m... 项目仓库:https://github.com/apachecn/m... 关于卷积神经网络:认领:1/12,校对:1/12 章节贡献者进度关于卷积神经网络--1@daewis100%2.1.1-2.1.3 2.1.4-2.1.6 2.2.1 2.2.2-2.2.3 2.3-2.4 3.1 3.2 3.3 3.4-3.5 4.1 4.2 写给不耐烦程序员的 JavaScript(校对)参与方式:https://github.com/apachecn/i... ...

June 15, 2019 · 5 min · jiezi

明白动态规划Dijkstra方法的Python实现和问题的解决步骤译

原作者:金子冴校阅:内野良一翻译:叶子原文链接目录什么是动态规划(Dynamic Programming)例题:用Dijkstra的方法解决最短路径问题(Python实现)使用动态规划解决问题的步骤参考什么是动态规划(Dynamic Programming)动态规划概要动态规划是一种解题手法的总称。它通过将一个无法解决的大问题分解成复数个小问题(也叫子问题),然后在解决这些小问题的基础之上来解决原始的大问题。通过使用动态规划,我们能将一部分在多项式时间内无法解决的问题,在类似多项式的时间内求得最优解(稍后会进行说明)。判断一个问题是否可以通过动态规划来解决的时,我们需要判断该问题是否满足可分治(分而治之)和可记忆(将阶段性成果进行缓存,便于重复利用)两个条件。首先,让我们先去理解:多项式时间、分而治之、以及记忆化(Memoization)。 什么是多项式时间,什么是多项式时间算法多项式时间是指由多项式表示的计算时间。多项式时间算法是指当入力的大小(长度或者个数)是n的时候,计算时间(执行步数)的上限在n的多项式时间内能够表示的算法。比如,计算九九乘法表的算法的计算时间可以表示为9x9。将其扩展到nxn的时候,计算时间用大O记法来表示的话,可以表示为O(n2)。这表明该算法的计算时间的上限可以用n2来表示,因此计算nxn的乘法的算法可以说是多项式算法。但是,在多项式时间内无法解决的问题也是存在的,比如说接下来将要说明的最短路径问题,在多项式时间内就无法解决。如下图所示的加权路线图,找一个从START开始到到达GOAL的花费最短(权重最小)的路线。 为了求最短路线,我们需要考虑全部路线的排列组合,在此基础之上进行花费的计算,要使得花费最小,那就需要找到最短的路径。像这样的问题,入力的规模每增大一点,路线的组合就呈指数级增加,因此计算全部路线的花费是不现实的。但是,如果使用了动态规划,就可以求得类似最短路径这样的在多项式时间内无法解决的问题的最优解。计算时会使用分而治之和记忆化两种手法。 什么是分而治之(分治)分治指的是将目标问题分割成复数个子问题的手法。让我们试着将刚才提到的最短路径问题进行子问题分解。对于刚才提到的例子,首先不要去考虑从START开始能够到达END的所有路线,而应该只考虑在某个时间点能够推进的路线。所以对于最开始的路线,只需要考虑START到a,b,c,d这四条。考虑到我们要解决的是最短路径的问题,这里我们选择从START开始花费最小的START->b路线开始。接着,我们只需考虑从b点出发能够推进的路线,这里我们也是选择花费最少的路线,b->g路线。 像这样,将一个需要考虑全部路径的问题转换为只考虑某个时间点能够推进的路线的问题(子问题)的分治手法,叫做分而治之。 什么是记忆化记忆化是指将计算结果保存到内存上,之后再次利用的手法。作为解释记忆化的例子,让我们来思考一下斐波那契数列的问题。这里我们省略斐波那契数列数列的说明。使用python进行斐波那契数列计算的场合,代码编写如下所示: 清单1 CulcFibonacci.py import sys# フィボナッチ数の計算def culc_fibonacci(n): if n > 1: return culc_fibonacci(n-1) + culc_fibonacci(n-2) elif n == 1: return 1 else: return 0def main(): # 1~10番目フィボナッチ数列を表示 # ⇒ 0, 1, 1, 2, 3, 5, 8, 13, 21, 34 for n in range(10): fibonacci_n = culc_fibonacci(n) print(fibonacci_n, end='') if not n == 9: print(', ', end='')if __name__ == '__main__': main() sys.exit(0)但是,清单1所示代码,在计算n=10的时候,必须去计算n=9~1,因此计算时间是O(n:的n次幂)(:实数),所以当n变大的时候,相关的计算量会呈指数级增长。下图表示的是斐波那契数列的计算过程。从下图我们可以看出,除了f(10)之外的所有计算都不止一次。 ...

June 7, 2019 · 1 min · jiezi

Crowdin-使用指南

作者:飞龙创建项目一、Github 登录 二、之后会跳到个人信息页,点击左上角的Projects -> Create Projects来创建项目。 三、在Project name填写项目名称,注意是全站唯一名称,和 Github 不一样。如果重复了可以加上apachecn-前缀。 如果你想让大家都看到的话,选择Public project,就和 Github 一样。 四、拉到下面选择语言,源语言Source language是英语不用管,目标语言Target language选简体中文(Simplified Chinese)。然后拉到最下面,点击Create创建项目。 上传文件一、访问项目主页(https://crowdin.com/project/<proj>),这里以apachecn为例。 二、点击右上角的Settings,再点击Files,跳到文件页面。也可以直接输入https://crowdin.com/project/<proj>/settings#files。 三、点击Add File来上传文件。我这里选了个英文的 HTML,上传成功。 翻译一、在项目主页,或设置页面中,点击Translations -> Chinese Simplified,再点击上传的文件。 二、等一小会儿之后,进入翻译页面,TM AND MT是机翻结果,可以参考,Enter Trans...处输入翻译,点SAVE保存。 三、可以立即在左侧看到翻译的内容。

May 28, 2019 · 1 min · jiezi

谷歌发布的Translatotron是什么

背景介绍作为中国人,学好英语这件事从小学开始就让人苦恼,近些年随着AI的快速发展,语言差异是否会缩小甚至被消灭成了热门话题。在5月15日,谷歌AI在博客平台发出一篇文章,正式介绍了一款能保留原声的“同声传译”黑科技,消息一出,迅速席卷网络,为科技发烧友带来了更多曙光,下面,让我们来揭开这个叫做“Translatoron”的神秘面纱。 Translatotron的出现目前市面常用的语音翻译产品的翻译过程包含三个步骤,首先将语音转换为文字,再由机器将文字转换为目标语言文字,最后将目标语言的文字转化为语音(文字转语音全称Text-To-Speech,也叫TTS)。 而谷歌的Translatotron有很大不同,它通过某种手段实现了语音到语音的直接转译,避免了上述的三个步骤。除此之外,它还有一些其它的优势,比如更快的推理速度、更容易识别不需要翻译的名称及专业名词等,最牛的是它可以保留原说话人的声音特征,做到原声输出其它语言,幻想一下用自己的声音说出了连本人都听不懂的外语,是不是有点像《流浪地球》中的同声翻译,多刺激啊,在此向大刘致敬! Translatotron的原理其实端到端的语音翻译在2016年就出现了,当时研究者们发现用“序列到序列模型”来做“语音到文字”的翻译可行性很高,到了2017年,研究者们证明出它果然很吊,但是这还不够,Translatotron的出现又向大家证明了“序列到序列模型”不仅可以转文字,还可以不依赖任何中间文本,直接帮你转为语音。 上面部分的名词有些含糊不清,这里来解释一下,首先是端到端学习,英文名为end-to-end,它就像一个黑盒子,人们把数据丢进去后只关心结果是否与期望的结果一致,不关心中间的实现过程。这个黑盒子的实现原理是,当结果和期望的结果出现误差后,它会将误差反传回训练模型的每一环节,让它们根据误差来自我调节,直到结果与预期相符为止。 而序列到序列模型,英文为Sequence to Sequence,它是端到端理念的一种实现框架,最早出现于Bengio在2014年的论文,Bengio是蒙特利尔大学的教授,他与另外两位朋友被AI领域的人戏称为“加拿大黑手党”。 “序列到序列”模型的目的是“将一个领域(比如中文)的序列转化为另一个领域(比如英文)的序列”,它是通过联合两个循环神经网络(RNN)来实现的,而联合的这种结构又被叫做编码-解码(Encoder-Decoder)结构,结构的两端支持多种数据类型,比如文字、语音、图像、视频等,非常适用于机器翻译。 Translatotron正是利用了图像这种数据类型,它通过声谱图作为输入,再生成出目标语言的声谱图,然后通过一个叫做Vocoder的语音编解码器(用于分析和合成用于音频数据压缩,多路复用,语音加密,语音转换等的人类语音信号)将新生成的光谱图转换为时域波形(一种表达信号与时间关系的波浪形状)。另外,它还可以选择使用一个编码器在合成翻译语音中维护原来的语音特征。 这项研究是由谷歌大脑、谷歌翻译和谷歌语音团队共同完成的,由于目前的训练数量较少,Translatotron所展示出的翻译质量以及原声匹配度没有预想中那么好,但随着更多数据的训练相信会有非常光明的前景。感兴趣的同学可以去官方博客了解一下。 如果你愿意,让我来帮你关注那些可能不知道又想知道却想不到的知识。

May 18, 2019 · 1 min · jiezi

免费使用谷歌的翻译接口

引言最近做个东西,需将各种语言翻译成中文,看了各家的翻译效果,还是谷歌的最好。 但谷歌的未提供免费接口,研究了谷歌的翻译页面,输入内容后会触发ajax请求,请求参数中除了输入内容,还有个加密参数tk,该加密算法在压缩的js代码中,我也在网上找到了网友摘出来的代码,js格式,一大段,压缩代码翻译起来很吃力,遂未翻译,而另辟蹊径,在生产环境的docker中打包了node环境,业务代码通过shell调用这段js,得到加密参数后再模拟请求,获得翻译结果,用着还挺好。 没过多久,发现失效了,请求返回403 Forbidden,禁止访问 ????,估计加密参数算法又升级了。 接口翻译固然好用,但隔断时间就要重新搞一次加密算法,这个就有点儿难以接受了,每次都要从大量压缩js代码中找出加密算法,还不一定能完全找对。 至此,我们的主角无头浏览器puppeteer就要登场了,puppeteer一个node类库,提供了简洁的API,可以让使用者操作chrome浏览器,基本可以完全模拟人的操作,比如打开页面、输入网址、等待页面指定内容加载、点击按钮、甚至滑动也可以,有了这个工具模拟用户翻译然后获取结果完全没问题。 获取翻译结果通过js获取分析了谷歌翻译页面的元素,发现用户输入内容的时候会触发某些按钮变灰,等到翻译完成,按钮会再次变亮,这其实是通过添加去除*-disabled类来实现的,所以当我们模拟输入之后等待该类消失即可 await page.waitForSelector('selector-ele-disabled', {hidden: true});待元素变亮(去除了*-disabled类),就可以从结果输入框中获取到结果了。 但这样实现起来比较麻烦,也不够直接,还需要puppeteer调用chrome的js执行环境去获取,获取的也不是原始的接口返回数据。因此通过查阅文档找到了下面更好的方法????。 通过拦截请求返回获取前段时间研究了如何爬取手机app中的数据,里面用到了中间人代理攻击,中间人代理转发请求、返回,转发的时候就可以对请求进行拦截处理,我就想puppeteer应该也有,果然查到了event-response,他是Page实例的一个钩子,如果我们设置了"response": function callback(response){},当chrome发出的任何一个请求返回的时候,都会触发他,并将类Response的一个实例传给回调函数,里面包含请求url、请求结果、请求结果状态等信息,这样我们就可以检测我们的翻译接口了 let browser = await puppeteer.launch()let page = await browser.newPage()page.on('response', async response => { const url = response.url() if (url.indexOf("检测的接口地址") != -1) { let text = await response.text() // text就是接口返回的结果,拿到接口原始数据,接下来就任你处理了 }})设计大体流程如下图所示,初始化实例,等待请求,请求到达之后模拟输入,然后返回结果,再次进入等待请求状态。 此文的最终目的是可以为调用者提供一个简洁的接口,请求该接口返回,返回为中文的结果,接口的响应时间尽可能的短,可支持并发。 响应时间没多少可以优化的地方,主要依赖网络环境,以及谷歌的接口响应时间,我们只能做到当谷歌接口返回的时候我们也第一时间返回给调用者。 并发这里可以做优化,一个puppeteer同一时间只能处理一个翻译请求,如果做个实例池,维护多个puppeteer实例,这样就可以提升翻译接口的并发能力了。 实例池如下图所示,虚线框内表示一个实例池,实例池中有多个puppeteer实例,他们之间互相独立,当请求来的时候,随机从池子中拿出一个实例,处理请求,等待请求处理完毕之后,再次将改实例放回池子中。 为了减少意外情况,池子中的每个实例处理100个翻译之后推出,重新启动一个新的额实例补充进来,池子中的实例总量保持不变,如果需要甚至可以搞成动态的,像php-fpm一样,请求多的时候动态增加实例池中的实例,空闲的时候,清理推出一些实例。 如何将请求和结果联系起来一个请求可以分为两个流程,一个请求流程,一个ajax成功回调流程,请求时候输入翻译原始内容,实例内部在请求谷歌ajax接口成功的时候调用预先注册好的回调函数,这两个流程没有办法直接联系起来,但他们都会接触到同一个实例,所以用这个实例将他们俩联系起来,ajax流程成功之后写入一个变量到实例对象上,请求流程中监测该实例上的变量,有数据说明请求成功,返回数据,清空该变量,原理可以看下面的简化代码 let obj = {}setTimeout(() => { obj.result = "this is async result"}, 2000)async function sleep(duration) { return new Promise(resolve => { setTimeout(() => { resolve() }, duration) })}async function getRet() { let times = 1 while(times <= 100) { if (obj.result) { return Promise.resolve(obj.result) } else { await sleep(200) } times++ }}(async () => { let ret = await getRet() console.log(ret) console.log("now i can do something")})()实现我将这个功能包装成了一个类库,上传到了npm,google-trans-api,顺便也熟悉了整个打包流程以及typescript的使用,不得不说typescript真是不错,可以防止很多误写的错误,还有自动提示的功能,用起来不要太爽。这里是源码地址aizuyan/google-trans-api。 ...

May 12, 2019 · 2 min · jiezi

ApacheCN-翻译活动进度公告-201953

Special Sponsors 我们组织了一个开源互助平台,方便开源组织和大 V 互相认识,互相帮助,整合资源。请回复这个帖子并注明组织/个人信息来申请加入。如果大家遇到了做得不错的教程或翻译项目,请回复这个帖子推荐给我们。我们会联系项目的维护者,一起把它变得更好。我们的公众号接受大家的投稿。将文章链接通过消息发给公众号,我们会和你联系。请大家回复这个帖子来推荐希望翻译的内容。我们近期有制作 Kaggle 教学视频的计划,如果你有兴趣参与,请联系片刻(QQ 529815144)。CS224n 2019 版笔记等到中文字幕翻译完毕后再整理。为了能够将开源事业做大做强,ApacheCN 需要与公益基金会(IT、教育类)合作,欢迎大家提供帮助。开源不是一个人的事情,我们的各个模块(请见组织架构)都需要人手来维护。如果你有兴趣参与,请联系片刻(QQ 529815144)。权限分配灵活,能者居之。写给不耐烦程序员的 JavaScript参与方式:https://github.com/apachecn/i... 整体进度:https://github.com/apachecn/i... 项目仓库:https://github.com/apachecn/i... 认领:0/42,翻译:0/42 章节贡献者进度1.关于本书(ES2019 版) 2.常见问题:本书 3. JavaScript 的历史和演变 4.常见问题:JavaScript 5.概览 6.语法 7.在控制台上打印信息(console.*) 8.断言 API 9.测验和练习入门 10.变量和赋值 11.值 12.运算符 13.非值undefined和null 14.布尔值 15.数字 16. Math 17. Unicode - 简要介绍(高级) 18.字符串 19.使用模板字面值和标记模板 20.符号 21.控制流语句 22.异常处理 23.可调用值 24.模块 25.单个对象 26.原型链和类 27.同步迭代 28.数组(Array) 29.类型化数组:处理二进制数据(高级) 30.映射(Map) 31. WeakMaps(WeakMap) 32.集(Set) 33. WeakSets(WeakSet) 34.解构 35.同步生成器(高级) 36. JavaScript 中的异步编程 37.异步编程的 Promise 38.异步函数 39.正则表达式(RegExp) 40.日期(Date) 41.创建和解析 JSON(JSON) 42.其余章节在哪里? seaborn 0.9 中文文档参与方式:https://github.com/apachecn/s... ...

May 3, 2019 · 3 min · jiezi

ApacheCN-翻译活动进度公告-2019423

Special Sponsors 我们是一个大型开源社区,旗下 QQ 群共 9000 余人,Github Star 数量超过 20k 个,在所有 Github 组织中排名前 200,网站日 uip 超过 4k,拥有 CSDN 博客专家和简书程序员优秀作者认证。我们组织公益性的翻译活动、学习活动和比赛组队活动,并和 DataWhale、LinuxStory 等国内著名开源组织保持良好的合作关系。 与商业组织不同,我们并不会追逐热点,或者唯利是图。作为公益组织,我们将完成项目放在首要位置,并有足够时间把项目打磨到极致。我们希望做出广大 AI 爱好者真正需要的东西,打造真正有价值的长尾作品。【主页】apachecn.org【归档】home.apachecn.org【社区】bbs.apachecn.org【Github】@ApacheCN【知识星球】ApacheCN AILearning自媒体平台 微博:@ApacheCN知乎专栏:AILearning公众号:ApacheCNCSDN | OSChina | 博客园简书 | 搜狐号 | bilibili 专栏We are ApacheCN Open Source Organization, not ASF! We are fans of AI, and have no relationship with ASF!合作 or 侵权,请联系 <apachecn@163.com> | 请抄送一份到 <wizard.z@foxmail.com>seaborn 0.9 中文文档参与方式:https://github.com/apachecn/s... 整体进度:https://github.com/apachecn/s... 项目仓库:https://github.com/apachecn/s... 认领:36/74,翻译:13/74 序号章节译者进度1An introduction to seaborn@yiran7324100%2Installing and getting started@neolei100%3Visualizing statistical relationships@JNJYan100%4Plotting with categorical data@hold2010 5Visualizing the distribution of a dataset@alohahahaha100%6Visualizing linear relationships@friedhelm739 7Building structured multi-plot grids@keyianpai100%8Controlling figure aesthetics 9Choosing color palettes@Modrisco100%10seaborn.relplot 11seaborn.scatterplot@tututwo 12seaborn.lineplot@tututwo 13seaborn.catplot@LIJIANcoder97 14seaborn.stripplot@LIJIANcoder97 15seaborn.swarmplot@LIJIANcoder97 16seaborn.boxplot 17seaborn.violinplot 18seaborn.boxenplot 19seaborn.pointplot 20seaborn.barplot@melon-bun 21seaborn.countplot 22seaborn.jointplot 23seaborn.pairplot 24seaborn.distplot 25seaborn.kdeplot 26seaborn.rugplot 27seaborn.lmplot 28seaborn.regplot 29seaborn.residplot 30seaborn.heatmap 31seaborn.clustermap 32seaborn.FacetGrid 33seaborn.FacetGrid.map 34seaborn.FacetGrid.map_dataframe 35seaborn.PairGrid 36seaborn.PairGrid.map 37seaborn.PairGrid.map_diag 38seaborn.PairGrid.map_offdiag 39seaborn.PairGrid.map_lower 40seaborn.PairGrid.map_upper 41seaborn.JointGrid 42seaborn.JointGrid.plot 43seaborn.JointGrid.plot_joint 44seaborn.JointGrid.plot_marginals 45seaborn.set 46seaborn.axes_style 47seaborn.set_style 48seaborn.plotting_context 49seaborn.set_context 50seaborn.set_color_codes 51seaborn.reset_defaults 52seaborn.reset_orig 53seaborn.set_palette@Modrisco100%54seaborn.color_palette@Modrisco100%55seaborn.husl_palette@Modrisco100%56seaborn.hls_palette@Modrisco100%57seaborn.cubehelix_palette@Modrisco100%58seaborn.dark_palette@Modrisco100%59seaborn.light_palette@Modrisco100%60seaborn.diverging_palette@Modrisco 61seaborn.blend_palette@Modrisco 62seaborn.xkcd_palette@Modrisco 63seaborn.crayon_palette@Modrisco 64seaborn.mpl_palette@Modrisco 65seaborn.choose_colorbrewer_palette@Modrisco 66seaborn.choose_cubehelix_palette@Modrisco 67seaborn.choose_light_palette@Modrisco 68seaborn.choose_dark_palette@Modrisco 69seaborn.choose_diverging_palette@Modrisco 70seaborn.load_dataset@Modrisco 71seaborn.despine@Modrisco 72seaborn.desaturate@Modrisco 73seaborn.saturate@Modrisco 74seaborn.set_hls_values@Modrisco Git 中文参考(校对)参与方式:https://github.com/apachecn/g... ...

April 23, 2019 · 2 min · jiezi

[译文] JavaScript工作原理:引擎、运行时、调用栈概述

[译文] JavaScript工作原理:引擎、运行时、调用栈概述原文 How JavaScript works: an overview of the engine, the runtime, and the call stack 随着 JavaScript 越来越流行,开发团队也更多地利用其来支持技术栈的各方面,前端、后端、混合应用、嵌入式设备等。 本文是旨在深入挖掘 JavaScript 其工作原理系列教程的首篇:我们认为通过了解 JavaScript 的构建单元并熟悉它们是怎样结合起来的,有助于你写出更好的代码和应用。我们也会分享一些在构建 SessionStack 应用时用到的经验法则,为了维持其竞争力它是一个健壮、高性能的轻量级 JavaScript 应用。 如GitHut stats所示,JavaScript 在活跃仓库数和GitHub总推送数方面位于首位。在其他类别排名中落后的也不多。 (查看最新的统计)。 如果项目变得如此依赖 JavaScript ,这就意味着开发者必须更加深入地理解其内部原理以充分利用语言和其生态提供的所有内容,从而构建更棒的软件。 事实显示,许多开发者每天都在使用 JavaScript 却不知其底层发生了什么。 概述几乎每个人都听说过 V8 引擎的概念,大多数人也知道 JavaScript 是单线程的或者使用回调队列。 在本文中,我们会详细讲解这些概念并阐述 JavaScript 是如何运行的。通过了解这些细节,你就可以利用提供的 APIs 写出更好的、无阻塞的应用。 如果你对 JavaScript 相对陌生,这个博客可以帮助你理解为何与其他语言相比 JavaScript 如此怪异。 如果你是位经验丰富的 JavaScript 开发人员,也希望能提供给你一些每天都在使用的 JavaScript 运行时实际运作机制的新见解。 JavaScript引擎JS引擎的一个最流行的例子就是谷歌的 V8。 V8 引擎使用在例如 Chrome 浏览器和 Node.js 中。下图是一个引擎组成部分的极简视图: ...

April 22, 2019 · 1 min · jiezi

【译】TypeScript中的React Render Props

原文链接: https://medium.com/@jrwebdev/…和之前的文章一样,本文也要求你对render props有一些知识背景,如果没有官方文档可能会对你有很大的帮助。本文将会使用函数作为children的render props模式以及结合React的context API来作为例子。如果你想使用类似于render这样子的render props,那也只需要把下面例子的children作为你要渲染的props即可。为了展示render props,我们将要重写之前文章的makeCounter HOC。这里先展示HOC的版本:export interface InjectedCounterProps { value: number; onIncrement(): void; onDecrement(): void;}interface MakeCounterProps { minValue?: number; maxValue?: number;}interface MakeCounterState { value: number;}const makeCounter = <P extends InjectedCounterProps>( Component: React.ComponentType<P>) => class MakeCounter extends React.Component< Subtract<P, InjectedCounterProps> & MakeCounterProps, MakeCounterState > { state: MakeCounterState = { value: 0, }; increment = () => { this.setState(prevState => ({ value: prevState.value === this.props.maxValue ? prevState.value : prevState.value + 1, })); }; decrement = () => { this.setState(prevState => ({ value: prevState.value === this.props.minValue ? prevState.value : prevState.value - 1, })); }; render() { const { minValue, maxValue, …props } = this.props; return ( <Component {…props as P} value={this.state.value} onIncrement={this.increment} onDecrement={this.decrement} /> ); } };HOC向组件注入了value和两个回调函数(onIncrement 和 onDecrement),此外还在HOC内部使用minValue和maxValue两个props而没有传递给组件。我们讨论了如果组件需要知道这些值,如何不传递props可能会出现问题,并且如果使用多个HOC包装组件,注入的props的命名也可能与其他HOC注入的props冲突。makeCounter HOC将会被像下面这样重写:interface InjectedCounterProps { value: number; onIncrement(): void; onDecrement(): void;}interface MakeCounterProps { minValue?: number; maxValue?: number; children(props: InjectedCounterProps): JSX.Element;}interface MakeCounterState { value: number;}class MakeCounter extends React.Component<MakeCounterProps, MakeCounterState> { state: MakeCounterState = { value: 0, }; increment = () => { this.setState(prevState => ({ value: prevState.value === this.props.maxValue ? prevState.value : prevState.value + 1, })); }; decrement = () => { this.setState(prevState => ({ value: prevState.value === this.props.minValue ? prevState.value : prevState.value - 1, })); }; render() { return this.props.children({ value: this.state.value, onIncrement: this.increment, onDecrement: this.decrement, }); }}这里有一些需要注意的变化。首先,injectedCounterProps被保留,因为我们需要定义一个props的interface在render props函数调用上而不是传递给组件的props(和HOC一样)。MakeCounter(MakeCounterProps)的props已经改变,加上以下内容:children(props: InjectedCounterProps): JSX.Element;这是render prop,然后组件内需要一个函数带上注入的props并返回JSX element。下面是它用来突出显示这一点的示例:interface CounterProps { style: React.CSSProperties; minValue?: number; maxValue?: number;}const Counter = (props: CounterProps) => ( <MakeCounter minValue={props.minValue} maxValue={props.maxValue}> {injectedProps => ( <div style={props.style}> <button onClick={injectedProps.onDecrement}> - </button> {injectedProps.value} <button onClick={injectedProps.onIncrement}> + </button> </div> )} </MakeCounter>);MakeCounter自己的组件声明变得简单多了;它不再被包装在函数中,因为它不再是临时的,输入也更加简单,不需要泛型、做差值和类型的交集。它只有简单的MakeCounterProps和MakeCounterState,就像其他任何组成部分一样:class MakeCounter extends React.Component< MakeCounterProps, MakeCounterState>最后,render()的工作也变少了;它只是一个函数调用并带上注入的props-不需要破坏和对象的props扩展运算符展开了!return this.props.children({ value: this.state.value, onIncrement: this.increment, onDecrement: this.decrement,});然后,render prop组件允许对props的命名和在使用的灵活性上进行更多的控制,这是和HOC等效的一个问题:interface CounterProps { style: React.CSSProperties; value: number; minCounterValue?: number; maxCounterValue?: number;}const Counter = (props: CounterProps) => ( <MakeCounter minValue={props.minCounterValue} maxValue={props.maxCounterValue} > {injectedProps => ( <div> <div>Some other value: {props.value}</div> <div style={props.style}> <button onClick={injectedProps.onDecrement}> - </button> {injectedProps.value} <button onClick={injectedProps.onIncrement}> + </button> </div> {props.minCounterValue !== undefined ? ( <div>Min value: {props.minCounterValue}</div> ) : null} {props.maxCounterValue !== undefined ? ( <div>Max value: {props.maxCounterValue}</div> ) : null} </div> )} </MakeCounter>);有了所有这些好处,特别是更简单的输入,那么为什么不一直使用render props呢?当然可以,这样做不会有任何问题,但要注意render props组件的一些问题。首先,这里有一个关注点以外的问题;MakeCounter组件现在被放在了Counter组件内而不是包装了它,这使得隔离测试这两个组件更加困难。其次,由于props被注入到组件的渲染函数中,因此不能在生命周期方法中使用它们(前提是计数器被更改为类组件)。这两个问题都很容易解决,因为您可以使用render props组件简单地生成一个新组件:interface CounterProps extends InjectedCounterProps { style: React.CSSProperties;}const Counter = (props: CounterProps) => ( <div style={props.style}> <button onClick={props.onDecrement}> - </button> {props.value} <button onClick={props.onIncrement}> + </button> </div>);interface WrappedCounterProps extends CounterProps { minValue?: number; maxValue?: number;}const WrappedCounter = ({ minValue, maxValue, …props}: WrappedCounterProps) => ( <MakeCounter minValue={minValue} maxValue={maxValue}> {injectedProps => <Counter {…props} {…injectedProps} />} </MakeCounter>);另一个问题是,一般来说,它不太方便,现在使用者需要编写很多样板文件,特别是如果他们只想将组件包装在一个单独的临时文件中并按原样使用props。这可以通过从render props组件生成HOC来补救:import { Subtract, Omit } from ‘utility-types’;import MakeCounter, { MakeCounterProps, InjectedCounterProps } from ‘./MakeCounter’;type MakeCounterHocProps = Omit<MakeCounterProps, ‘children’>;const makeCounter = <P extends InjectedCounterProps>( Component: React.ComponentType<P>): React.SFC<Subtract<P, InjectedCounterProps> & MakeCounterHocProps> => ({ minValue, maxValue, …props}: MakeCounterHocProps) => ( <MakeCounter minValue={minValue} maxValue={maxValue}> {injectedProps => <Component {…props as P} {…injectedProps} />} </MakeCounter>);在这里,上一篇文章的技术,以及render props组件的现有类型,被用来生成HOC。这里唯一需要注意的是,我们必须从HOC的props中移除render prop(children),以便在使用时不暴露它:type MakeCounterHocProps = Omit<MakeCounterProps, ‘children’>;最后,HOC和render props组件之间的权衡归结为灵活性和便利性。这可以通过首先编写render props组件,然后从中生成HOC来解决,这使使用者能够在两者之间进行选择。这种方法在可重用组件库中越来越常见,例如优秀的render-fns库。就TypeScript而言,毫无疑问,hocs的类型定义要困难得多;尽管通过这两篇文章中的示例,它表明这种负担是由HOC的提供者而不是使用者承担的。在使用方面,可以认为使用HOC比使用render props组件更容易。在react v16.8.0之前,我建议使用render props组件以提高键入的灵活性和简单性,如果需要,例如构建可重用的组件库,或者对于简单在项目中使用的render props组件,我将仅从中生成HOC。在react v16.8.0中释放react hook之后,我强烈建议在可能的情况下对两个高阶组件或render props使用它们,因为它们的类型更简单。 ...

April 18, 2019 · 3 min · jiezi

【译】TypeScript中的React高阶组件

原文链接:https://medium.com/@jrwebdev/…高阶组件(HOCs)在React中是组件复用的一个强大工具。但是,经常有开发者在结合TypeScript使用中抱怨道很难去为其设置types。这边文章将会假设你已经具备了HOCs的基本知识,并会根据由浅入深的例子来向你展示如何去为其设置types。在本文中,高阶组件将会被分为两种基本模式,我们将其命名为enhancers和injectors:enhancers:用附加的功能/props来包裹组件。injectors:向组件注入props。请注意,本文中的示例并不是最佳实践,本文主要只是展示如何在HOCs中设置types。Enhancers我们将从enhancers开始,因为它更容易去设置types。此模式的一个基本示例是一个向组件添加loading props的HOC,并且将其设置为true的时候展示loading图。下面是一个没有types的示例:const withLoading = Component => class WithLoading extends React.Component { render() { const { loading, …props } = this.props; return loading ? <LoadingSpinner /> : <Component {…props} />; } };然后是加上typesinterface WithLoadingProps { loading: boolean;}const withLoading = <P extends object>(Component: React.ComponentType<P>) => class WithLoading extends React.Component<P & WithLoadingProps> { render() { const { loading, …props } = this.props; return loading ? <LoadingSpinner /> : <Component {…props as P} />; } };这里发生了一些事情,所以我们将把它分解:interface WithLoadingProps { loading: boolean;}在这里,声明一个props的interface,将会被添加到被包裹的组件上。<P extends object>(Component: React.ComponentType<P>)这里我们使用泛型:P表示传递到HOC的组件的props。React.ComponentType<P> 是 React.FunctionComponent<P> | React.ClassComponent<P>的别名,表示传递到HOC的组件可以是类组件或者是函数组件。class WithLoading extends React.Component<P & WithLoadingProps>在这里,我们定义从HOC返回的组件,并指定该组件将包括传入组件的props(P)和HOC的props(WithLoadingProps)。它们通过 & 组合在一起。const { loading, …props } = this.props;最后,我们使用loading props有条件地显示加loading图或传递了自己props的组件:return loading ? <LoadingSpinner /> : <Component {…props as P} />;注意:由于typescript中可能存在的bug,因此从typescript v3.2开始,这里需要类型转换(props as p)。我们的withloading HOC也可以重写以返回函数组件而不是类:const withLoading = <P extends object>( Component: React.ComponentType<P>): React.FC<P & WithLoadingProps> => ({ loading, …props}: WithLoadingProps) => loading ? <LoadingSpinner /> : <Component {…props as P} />;这里,我们对对象rest/spread也有同样的问题,因此通过设置显式的返回类型React.FC<P & WithLoadingProps>来解决这个问题,但只能在无状态功能组件中使用WithLoadingProps。注意:React.FC是React.FunctionComponent的缩写。在早期版本的@types/react中,是React.SFC或React.StatelessFunctionalComponent。Injectorsinjectors是更常见的HOC形式,但更难为其设置类型。除了向组件中注入props外,在大多数情况下,当包裹好后,它们也会移除注入的props,这样它们就不能再从外部设置了。react redux的connect就是是injector HOC的一个例子,但是在本文中,我们将使用一个更简单的例子,它注入一个计数器值并回调以增加和减少该值:import { Subtract } from ‘utility-types’;export interface InjectedCounterProps { value: number; onIncrement(): void; onDecrement(): void;}interface MakeCounterState { value: number;}const makeCounter = <P extends InjectedCounterProps>( Component: React.ComponentType<P>) => class MakeCounter extends React.Component< Subtract<P, InjectedCounterProps>, MakeCounterState > { state: MakeCounterState = { value: 0, }; increment = () => { this.setState(prevState => ({ value: prevState.value + 1, })); }; decrement = () => { this.setState(prevState => ({ value: prevState.value - 1, })); }; render() { return ( <Component {…this.props as P} value={this.state.value} onIncrement={this.increment} onDecrement={this.decrement} /> ); } };这里有几个关键区别:export interface InjectedCounterProps { value: number; onIncrement(): void; onDecrement(): void;}我们给将要注入到组件的props声明一个interface,该接口将被导出,以便这些props可由被HOC包裹的组件使用:import makeCounter, { InjectedCounterProps } from ‘./makeCounter’;interface CounterProps extends InjectedCounterProps { style?: React.CSSProperties;}const Counter = (props: CounterProps) => ( <div style={props.style}> <button onClick={props.onDecrement}> - </button> {props.value} <button onClick={props.onIncrement}> + </button> </div>);export default makeCounter(Counter);<P extends InjectedCounterProps>(Component: React.ComponentType<P>)我们再次使用泛型,但是这次,你要确保传入到HOC的组件包含注入到其中的props,否则,你将收到一个编译错误。class MakeCounter extends React.Component< Subtract<P, InjectedCounterProps>, MakeCounterState >HOC返回的组件使用Piotrek Witek’s的utility-types包中的subtract,它将从传入组件的props中减去注入的props,这意味着如果它们设置在生成的包裹组件上,则会收到编译错误:Enhance + Inject结合这两种模式,我们将在计数器示例的基础上,允许将最小和最大计数器值传递给HOC,而HOC又被它截取并使用,而不将它们传递给组件:export interface InjectedCounterProps { value: number; onIncrement(): void; onDecrement(): void;}interface MakeCounterProps { minValue?: number; maxValue?: number;}interface MakeCounterState { value: number;}const makeCounter = <P extends InjectedCounterProps>( Component: React.ComponentType<P>) => class MakeCounter extends React.Component< Subtract<P, InjectedCounterProps> & MakeCounterProps, MakeCounterState > { state: MakeCounterState = { value: 0, }; increment = () => { this.setState(prevState => ({ value: prevState.value === this.props.maxValue ? prevState.value : prevState.value + 1, })); }; decrement = () => { this.setState(prevState => ({ value: prevState.value === this.props.minValue ? prevState.value : prevState.value - 1, })); }; render() { const { minValue, maxValue, …props } = this.props; return ( <Component {…props as P} value={this.state.value} onIncrement={this.increment} onDecrement={this.decrement} /> ); } };这里,Subtract与types交集相结合,将组件自身的props与HOCs自身的props相结合,减去注入组件的props:Subtract<P, InjectedCounterProps> & MakeCounterProps除此之外,与其他两种模式相比,没有真正的差异需要强调,但是这个示例确实带来了一些高阶组件的问题。这些并不是真正特定于typescript的,但值得详细说明,以便我们可以讨论如何使用typescript来解决这些问题。首先,MinValue和MaxValue被HOC拦截,而不是传递给组件。但是,你也许希望它们是这样的,这样你就可以基于这些值禁用递增/递减按钮,或者向用户显示一条消息。如果用HOC,你也可以简单地修改它来注入这些值,但是如果你没有(例如,它来自一个NPM包),这就将会是一个问题。其次,由HOC注入的prop有一个非常通用的名称;如果要将其用于其他目的,或者如果要从多个HOC注入prop,则此名称可能与其他注入的prop冲突。您可以将名称更改为不太通用的解决方案,但就解决方案而言,这不是一个很好的解决方案! ...

April 18, 2019 · 2 min · jiezi

ApacheCN 翻译活动进度公告 2019.4.15

我们是一个大型开源社区,旗下 QQ 群共 9000 余人,Github Star 数量超过 20k 个,在所有 Github 组织中排名前 200,网站日 uip 超过 4k,拥有 CSDN 博客专家和简书程序员优秀作者认证。我们组织公益性的翻译活动、学习活动和比赛组队活动,并和 DataWhale、LinuxStory 等国内著名开源组织保持良好的合作关系。 与商业组织不同,我们并不会追逐热点,或者唯利是图。作为公益组织,我们将完成项目放在首要位置,并有足够时间把项目打磨到极致。我们希望做出广大 AI 爱好者真正需要的东西,打造真正有价值的长尾作品。【主页】apachecn.org【归档】home.apachecn.org【社区】bbs.apachecn.org【Github】@ApacheCN【知识星球】ApacheCN AILearning自媒体平台微博:@ApacheCN知乎专栏:AILearning公众号:ApacheCNCSDN | OSChina | 博客园简书 | 搜狐号 | bilibili 专栏We are ApacheCN Open Source Organization, not ASF! We are fans of AI, and have no relationship with ASF!合作 or 侵权,请联系 <apachecn@163.com> | 请抄送一份到 <wizard.z@foxmail.com>seaborn 0.9 中文文档参与方式:https://github.com/apachecn/s…整体进度:https://github.com/apachecn/s…项目仓库:https://github.com/apachecn/s…认领:36/74,翻译:12/74序号章节译者进度1An introduction to seaborn@yiran7324100%2Installing and getting started@neolei100%3Visualizing statistical relationships@JNJYan100%4Plotting with categorical data@hold2010 5Visualizing the distribution of a dataset@alohahahaha100%6Visualizing linear relationships@friedhelm739 7Building structured multi-plot grids@keyianpai 8Controlling figure aesthetics 9Choosing color palettes@Modrisco100%10seaborn.relplot 11seaborn.scatterplot@tututwo 12seaborn.lineplot@tututwo 13seaborn.catplot@LIJIANcoder97 14seaborn.stripplot@LIJIANcoder97 15seaborn.swarmplot@LIJIANcoder97 16seaborn.boxplot 17seaborn.violinplot 18seaborn.boxenplot 19seaborn.pointplot 20seaborn.barplot@melon-bun 21seaborn.countplot 22seaborn.jointplot 23seaborn.pairplot 24seaborn.distplot 25seaborn.kdeplot 26seaborn.rugplot 27seaborn.lmplot 28seaborn.regplot 29seaborn.residplot 30seaborn.heatmap 31seaborn.clustermap 32seaborn.FacetGrid 33seaborn.FacetGrid.map 34seaborn.FacetGrid.map_dataframe 35seaborn.PairGrid 36seaborn.PairGrid.map 37seaborn.PairGrid.map_diag 38seaborn.PairGrid.map_offdiag 39seaborn.PairGrid.map_lower 40seaborn.PairGrid.map_upper 41seaborn.JointGrid 42seaborn.JointGrid.plot 43seaborn.JointGrid.plot_joint 44seaborn.JointGrid.plot_marginals 45seaborn.set 46seaborn.axes_style 47seaborn.set_style 48seaborn.plotting_context 49seaborn.set_context 50seaborn.set_color_codes 51seaborn.reset_defaults 52seaborn.reset_orig 53seaborn.set_palette@Modrisco100%54seaborn.color_palette@Modrisco100%55seaborn.husl_palette@Modrisco100%56seaborn.hls_palette@Modrisco100%57seaborn.cubehelix_palette@Modrisco100%58seaborn.dark_palette@Modrisco100%59seaborn.light_palette@Modrisco100%60seaborn.diverging_palette@Modrisco 61seaborn.blend_palette@Modrisco 62seaborn.xkcd_palette@Modrisco 63seaborn.crayon_palette@Modrisco 64seaborn.mpl_palette@Modrisco 65seaborn.choose_colorbrewer_palette@Modrisco 66seaborn.choose_cubehelix_palette@Modrisco 67seaborn.choose_light_palette@Modrisco 68seaborn.choose_dark_palette@Modrisco 69seaborn.choose_diverging_palette@Modrisco 70seaborn.load_dataset@Modrisco 71seaborn.despine@Modrisco 72seaborn.desaturate@Modrisco 73seaborn.saturate@Modrisco 74seaborn.set_hls_values@Modrisco Git 中文参考(校对)参与方式:https://github.com/apachecn/g…整体进度:https://github.com/apachecn/g…项目仓库:https://github.com/apachecn/g…认领:0/83,校对:0/83序号章节贡献者进度1git 2git-config 3git-help 4git-init 5git-clone 6git-add 7git-status 8git-diff 9git-commit 10git-reset 11git-rm 12git-mv 13git-branch 14git-checkout 15git-merge 16git-mergetool 17git-log 18git-stash 19git-tag 20git-worktree 21git-fetch 22git-pull 23git-push 24git-remote 25git-submodule 26git-show 27git-log 29git-shortlog 30git-describe 31git-apply 32git-cherry-pick 34git-rebase 35git-revert 36git-bisect 37git-blame 38git-grep 39gitattributes 40giteveryday 41gitglossary 42githooks 43gitignore 44gitmodules 45gitrevisions 46gittutorial 47gitworkflows 48git-am 50git-format-patch 51git-send-email 52git-request-pull 53git-svn 54git-fast-import 55git-clean 56git-gc 57git-fsck 58git-reflog 59git-filter-branch 60git-instaweb 61git-archive 62git-bundle 63git-daemon 64git-update-server-info 65git-cat-file 66git-check-ignore 67git-checkout-index 68git-commit-tree 69git-count-objects 70git-diff-index 71git-for-each-ref 72git-hash-object 73git-ls-files 74git-merge-base 75git-read-tree 76git-rev-list 77git-rev-parse 78git-show-ref 79git-symbolic-ref 80git-update-index 81git-update-ref 82git-verify-pack 83git-write-tree HBase 3.0 中文参考指南(校对)参与方式:https://github.com/apachecn/h…整体进度:https://github.com/apachecn/h…项目仓库:https://github.com/apachecn/h…认领:18/31,校对:11/31章节贡献者进度Preface@xixici100%Getting Started@xixici100%Apache HBase Configuration@xixici100%Upgrading@xixici100%The Apache HBase Shell@xixici100%Data Model@Winchester-Yi HBase and Schema Design@RaymondCode100%RegionServer Sizing Rules of Thumb HBase and MapReduce@BridgetLai100%Securing Apache HBase Architecture@RaymondCode In-memory Compaction@mychaow Backup and Restore@mychaow Synchronous Replication@mychaow Apache HBase APIs@xixici100%Apache HBase External APIs@xixici100%Thrift API and Filter Language@xixici100%HBase and Spark@TsingJyujing100%Apache HBase Coprocessors@TsingJyujing Apache HBase Performance Tuning Troubleshooting and Debugging Apache HBase Apache HBase Case Studies Apache HBase Operational Management Building and Developing Apache HBase Unit Testing HBase Applications Protobuf in HBase@TsingJyujing Procedure Framework (Pv2): HBASE-12439 AMv2 Description for Devs ZooKeeper Community Appendix UCB CS61b:Java 中的数据结构参与方式:https://github.com/apachecn/c…整体进度:https://github.com/apachecn/c…项目仓库:https://github.com/apachecn/c…认领:6/12,翻译:4/12标题译者进度一、算法复杂度@leader402二、抽象数据类型@Allenyep100%三、满足规范@renyuhuiharrison四、序列和它们的实现@biubiubiuboomboomboom100%五、树@biubiubiuboomboomboom100%六、搜索树 七、哈希 八、排序和选择@Rachel-Hu100%九、平衡搜索 十、并发和同步 十一、伪随机序列 十二、图 UCB Prob140:面向数据科学的概率论参与方式:https://github.com/apachecn/p…整体进度:https://github.com/apachecn/p…项目仓库:https://github.com/apachecn/p…认领:22/25,翻译:19/25标题译者翻译进度一、基础飞龙100%二、计算几率飞龙100%三、随机变量飞龙100%四、事件之间的关系@biubiubiuboomboomboom100%五、事件集合 >0%六、随机计数@viviwong100%七、泊松化@YAOYI626100%八、期望 50%九、条件(续)@YAOYI626100%十、马尔科夫链喵十八100%十一、马尔科夫链(续)喵十八100%十二、标准差缺只萨摩100%十三、方差和协方差缺只萨摩100%十四、中心极限定理喵十八100%十五、连续分布@ThunderboltSmile十六、变换@hellozhaihy十七、联合密度@Winchester-Yi100%十八、正态和 Gamma 族@Winchester-Yi100%十九、和的分布平淡的天100%二十、估计方法平淡的天100%二十一、Beta 和二项@lvzhetx100%二十二、预测 50%二十三、联合正态随机变量@JUNE951234二十四、简单线性回归@ThomasCai100%二十五、多元回归@lanhaixuan100%认领完毕OpenCV 4.0 中文教程参与方式:https://github.com/apachecn/o…整体进度:https://github.com/apachecn/o…项目仓库:https://github.com/apachecn/o…认领:51/51,翻译:24/51。Pytorch 1.0 中文文档参与方式:https://github.com/apachecn/p…整体进度:https://github.com/apachecn/p…项目仓库:https://github.com/apachecn/p…教程部分:认领:37/37,翻译:34/37;文档部分:认领:39/39,翻译:34/39Airflow 中文文档(校对)参与方式:https://github.com/apachecn/a…整体进度:https://github.com/apachecn/a…项目仓库:https://github.com/apachecn/a…认领:29/30,校对:27/30翻译征集要求:机器学习/数据科学相关或者编程相关原文必须在互联网上开放不能只提供 PDF 格式(我们实在不想把精力都花在排版上)请先搜索有没有人翻译过请回复这个帖子。赞助我们 ...

April 15, 2019 · 2 min · jiezi

ApacheCN 翻译活动进度公告 2019.4.7

我们是一个大型开源社区,旗下 QQ 群共 9000 余人,Github Star 数量超过 20k 个,网站日 uip 超过 4k,拥有 CSDN 博客专家和简书程序员优秀作者认证。我们组织公益性的翻译活动、学习活动和比赛组队活动,并和 DataWhale、LinuxStory 等国内著名开源组织保持良好的合作关系。 与商业组织不同,我们并不会追逐热点,或者唯利是图。作为公益组织,我们将完成项目放在首要位置,并有足够时间把项目打磨到极致。我们希望做出广大 AI 爱好者真正需要的东西,打造真正有价值的长尾作品。【主页】apachecn.org【归档】home.apachecn.org【社区】bbs.apachecn.org【Github】@ApacheCN自媒体平台微博:@ApacheCN知乎专栏:AILearning公众号:ApacheCNCSDN | OSChina | 博客园简书 | 搜狐号 | bilibili 专栏We are ApacheCN Open Source Organization, not ASF! We are fans of AI, and have no relationship with ASF!合作 or 侵权,请联系 <apachecn@163.com> | 请抄送一份到 <wizard.z@foxmail.com>seaborn 0.9 中文文档参与方式:https://github.com/apachecn/s…整体进度:https://github.com/apachecn/s…项目仓库:https://github.com/apachecn/s…认领:33/74,翻译:12/74序号章节译者进度1An introduction to seaborn@yiran7324100%2Installing and getting started@neolei100%3Visualizing statistical relationships@JNJYan100%4Plotting with categorical data@hold2010 5Visualizing the distribution of a dataset@alohahahaha100%6Visualizing linear relationships@friedhelm739 7Building structured multi-plot grids@keyianpai 8Controlling figure aesthetics 9Choosing color palettes@Modrisco100%10seaborn.relplot 11seaborn.scatterplot@tututwo 12seaborn.lineplot@tututwo 13seaborn.catplot 14seaborn.stripplot 15seaborn.swarmplot 16seaborn.boxplot 17seaborn.violinplot 18seaborn.boxenplot 19seaborn.pointplot 20seaborn.barplot@melon-bun 21seaborn.countplot 22seaborn.jointplot 23seaborn.pairplot 24seaborn.distplot 25seaborn.kdeplot 26seaborn.rugplot 27seaborn.lmplot 28seaborn.regplot 29seaborn.residplot 30seaborn.heatmap 31seaborn.clustermap 32seaborn.FacetGrid 33seaborn.FacetGrid.map 34seaborn.FacetGrid.map_dataframe 35seaborn.PairGrid 36seaborn.PairGrid.map 37seaborn.PairGrid.map_diag 38seaborn.PairGrid.map_offdiag 39seaborn.PairGrid.map_lower 40seaborn.PairGrid.map_upper 41seaborn.JointGrid 42seaborn.JointGrid.plot 43seaborn.JointGrid.plot_joint 44seaborn.JointGrid.plot_marginals 45seaborn.set 46seaborn.axes_style 47seaborn.set_style 48seaborn.plotting_context 49seaborn.set_context 50seaborn.set_color_codes 51seaborn.reset_defaults 52seaborn.reset_orig 53seaborn.set_palette@Modrisco100%54seaborn.color_palette@Modrisco100%55seaborn.husl_palette@Modrisco100%56seaborn.hls_palette@Modrisco100%57seaborn.cubehelix_palette@Modrisco100%58seaborn.dark_palette@Modrisco100%59seaborn.light_palette@Modrisco100%60seaborn.diverging_palette@Modrisco 61seaborn.blend_palette@Modrisco 62seaborn.xkcd_palette@Modrisco 63seaborn.crayon_palette@Modrisco 64seaborn.mpl_palette@Modrisco 65seaborn.choose_colorbrewer_palette@Modrisco 66seaborn.choose_cubehelix_palette@Modrisco 67seaborn.choose_light_palette@Modrisco 68seaborn.choose_dark_palette@Modrisco 69seaborn.choose_diverging_palette@Modrisco 70seaborn.load_dataset@Modrisco 71seaborn.despine@Modrisco 72seaborn.desaturate@Modrisco 73seaborn.saturate@Modrisco 74seaborn.set_hls_values@Modrisco Git 中文参考(校对)参与方式:https://github.com/apachecn/g…整体进度:https://github.com/apachecn/g…项目仓库:https://github.com/apachecn/g…认领:0/83,校对:0/83序号章节贡献者进度1git 2git-config 3git-help 4git-init 5git-clone 6git-add 7git-status 8git-diff 9git-commit 10git-reset 11git-rm 12git-mv 13git-branch 14git-checkout 15git-merge 16git-mergetool 17git-log 18git-stash 19git-tag 20git-worktree 21git-fetch 22git-pull 23git-push 24git-remote 25git-submodule 26git-show 27git-log 29git-shortlog 30git-describe 31git-apply 32git-cherry-pick 34git-rebase 35git-revert 36git-bisect 37git-blame 38git-grep 39gitattributes 40giteveryday 41gitglossary 42githooks 43gitignore 44gitmodules 45gitrevisions 46gittutorial 47gitworkflows 48git-am 50git-format-patch 51git-send-email 52git-request-pull 53git-svn 54git-fast-import 55git-clean 56git-gc 57git-fsck 58git-reflog 59git-filter-branch 60git-instaweb 61git-archive 62git-bundle 63git-daemon 64git-update-server-info 65git-cat-file 66git-check-ignore 67git-checkout-index 68git-commit-tree 69git-count-objects 70git-diff-index 71git-for-each-ref 72git-hash-object 73git-ls-files 74git-merge-base 75git-read-tree 76git-rev-list 77git-rev-parse 78git-show-ref 79git-symbolic-ref 80git-update-index 81git-update-ref 82git-verify-pack 83git-write-tree HBase 3.0 中文参考指南(校对)参与方式:https://github.com/apachecn/h…整体进度:https://github.com/apachecn/h…项目仓库:https://github.com/apachecn/h…认领:18/31,校对:11/31章节贡献者进度Preface@xixici100%Getting Started@xixici100%Apache HBase Configuration@xixici100%Upgrading@xixici100%The Apache HBase Shell@xixici100%Data Model@Winchester-Yi HBase and Schema Design@RaymondCode100%RegionServer Sizing Rules of Thumb HBase and MapReduce@BridgetLai100%Securing Apache HBase Architecture@RaymondCode In-memory Compaction@mychaow Backup and Restore@mychaow Synchronous Replication@mychaow Apache HBase APIs@xixici100%Apache HBase External APIs@xixici100%Thrift API and Filter Language@xixici100%HBase and Spark@TsingJyujing100%Apache HBase Coprocessors@TsingJyujing Apache HBase Performance Tuning Troubleshooting and Debugging Apache HBase Apache HBase Case Studies Apache HBase Operational Management Building and Developing Apache HBase Unit Testing HBase Applications Protobuf in HBase@TsingJyujing Procedure Framework (Pv2): HBASE-12439 AMv2 Description for Devs ZooKeeper Community Appendix UCB CS61b:Java 中的数据结构参与方式:https://github.com/apachecn/c…整体进度:https://github.com/apachecn/c…项目仓库:https://github.com/apachecn/c…认领:6/12,翻译:4/12标题译者进度一、算法复杂度@leader402二、抽象数据类型@Allenyep100%三、满足规范@renyuhuiharrison四、序列和它们的实现@biubiubiuboomboomboom100%五、树@biubiubiuboomboomboom100%六、搜索树 七、哈希 八、排序和选择@Rachel-Hu100%九、平衡搜索 十、并发和同步 十一、伪随机序列 十二、图 UCB Prob140:面向数据科学的概率论参与方式:https://github.com/apachecn/p…整体进度:https://github.com/apachecn/p…项目仓库:https://github.com/apachecn/p…认领:22/25,翻译:19/25标题译者翻译进度一、基础飞龙100%二、计算几率飞龙100%三、随机变量飞龙100%四、事件之间的关系@biubiubiuboomboomboom100%五、事件集合 >0%六、随机计数@viviwong100%七、泊松化@YAOYI626100%八、期望 50%九、条件(续)@YAOYI626100%十、马尔科夫链喵十八100%十一、马尔科夫链(续)喵十八100%十二、标准差缺只萨摩100%十三、方差和协方差缺只萨摩100%十四、中心极限定理喵十八100%十五、连续分布@ThunderboltSmile十六、变换@hellozhaihy十七、联合密度@Winchester-Yi100%十八、正态和 Gamma 族@Winchester-Yi100%十九、和的分布平淡的天100%二十、估计方法平淡的天100%二十一、Beta 和二项@lvzhetx100%二十二、预测 50%二十三、联合正态随机变量@JUNE951234二十四、简单线性回归@ThomasCai100%二十五、多元回归@lanhaixuan100%认领完毕OpenCV 4.0 中文教程参与方式:https://github.com/apachecn/o…整体进度:https://github.com/apachecn/o…项目仓库:https://github.com/apachecn/o…认领:51/51,翻译:24/51。Pytorch 1.0 中文文档参与方式:https://github.com/apachecn/p…整体进度:https://github.com/apachecn/p…项目仓库:https://github.com/apachecn/p…教程部分:认领:37/37,翻译:34/37;文档部分:认领:39/39,翻译:34/39Airflow 中文文档(校对)参与方式:https://github.com/apachecn/a…整体进度:https://github.com/apachecn/a…项目仓库:https://github.com/apachecn/a…认领:29/30,校对:27/30翻译征集要求:机器学习/数据科学相关或者编程相关原文必须在互联网上开放不能只提供 PDF 格式(我们实在不想把精力都花在排版上)请先搜索有没有人翻译过请回复这个帖子。赞助我们 ...

April 7, 2019 · 2 min · jiezi

【译】如何在React Hooks中获取数据?

原文链接: https://www.robinwieruch.de/r…在本教程中,我想通过state和effect hook来像你展示如何用React Hooks来获取数据。我将会使用Hacker News的API来获取热门的技术文章。你将会实现一个属于你自己的自定义hook来在你程序的任何地方复用,或者是作为一个npm包发布出来。如果你还不知道这个React的新特性,那么点击React Hooks介绍,如果你想直接查看最后的实现效果,请点击这个github仓库。注意:在未来,React Hooks将不会用于React的数据获取,一个叫做Suspense的特性将会去负责它。但下面的教程仍会让你去更多的了解关于React中的state和effect hook。用React Hooks去获取数据如果你对在React中获取数据还不熟悉,可以查看我其他的React获取数据的文章。它将会引导你通过使用React的class组件来获取数据,并且还可以和render props或者高阶组件一起使用,以及结合错误处理和加载状态。在这篇文章中,我将会在function组件中使用React Hooks来展示这些功能。import React, { useState } from ‘react’;function App() { const [data, setData] = useState({ hits: [] }); return ( <ul> {data.hits.map(item => ( <li key={item.objectID}> <a href={item.url}>{item.title}</a> </li> ))} </ul> );}export default App;这个App组件展示了一个包含很多项的list(hits = Hacker News 文章)。state和state的更新函数来自于state hook中useState的调用,它负责管理我们用来渲染list数据的本地状态,初始状态是一个空数组,此时还没有为其设置任何的状态。我们将使用axios来获取数据,当然你也可以使用其他的库或者fetch API,如果你还没安装axios,你可以在命令行使用npm install axios来安装它。然后来实现用于数据获取的effect hook:import React, { useState, useEffect } from ‘react’;import axios from ‘axios’;function App() { const [data, setData] = useState({ hits: [] }); useEffect(async () => { const result = await axios( ‘http://hn.algolia.com/api/v1/search?query=redux', ); setData(result.data); }); return ( <ul> {data.hits.map(item => ( <li key={item.objectID}> <a href={item.url}>{item.title}</a> </li> ))} </ul> );}export default App;通过axios在useEffect中获取数据,然后通过setData将数据放到组件本地的state中,并通过async/await来处理Promise。然而当你运行程序的时候,你应该会遇到一个讨厌的循环。effect hook不仅在组件mount的时候也会在update的时候运行。因为我们在每一次的数据获取之后,会去通过setState设置状态,这时候组件update然后effect就会运行一遍,这就造成了数据一次又一次的获取。我们仅仅是想要在组件mount的时候来获取一次数据,这就是为什么我们需要在useEffect的第二个参数提供一个空数组,从而实现只在mount的时候触发数据获取而不是每一次update。import React, { useState, useEffect } from ‘react’;import axios from ‘axios’;function App() { const [data, setData] = useState({ hits: [] }); useEffect(async () => { const result = await axios( ‘http://hn.algolia.com/api/v1/search?query=redux', ); setData(result.data); }, []); return ( <ul> {data.hits.map(item => ( <li key={item.objectID}> <a href={item.url}>{item.title}</a> </li> ))} </ul> );}export default App;第二个参数可以定义hooks所依赖的变量(在一个数组中去分配),如果一个变量改变了,hooks将会执行一次,如果是一个空数组的话,hooks将不会在组件更新的时候执行,因为它没有监听到任何的变量。这里还有一个陷阱,在代码中,我们使用async/await从第三方的API中获取数据,根据文档,每一个async函数都将返回一个promise,async函数声明定义了一个异步函数,它返回一个asyncFunction对象,异步函数是通过事件循环异步操作的函数,使用隐式Promise返回其结果。但是,effect hook应该不返回任何内容或清除功能,这就是为什么你会在控制台看到以下警告:07:41:22.910 index.js:1452 Warning: useEffect function must return a cleanup function or nothing. Promises and useEffect(async () => …) are not supported, but you can call an async function inside an effect.. 这就是为什么不允许在useEffect函数中直接使用async的原因。让我们通过在effect内部使用异步函数来实现它的解决方案。import React, { useState, useEffect } from ‘react’;import axios from ‘axios’;function App() { const [data, setData] = useState({ hits: [] }); useEffect(() => { const fetchData = async () => { const result = await axios( ‘http://hn.algolia.com/api/v1/search?query=redux', ); setData(result.data); }; fetchData(); }, []); return ( <ul> {data.hits.map(item => ( <li key={item.objectID}> <a href={item.url}>{item.title}</a> </li> ))} </ul> );}export default App;简而言之,这就是用React Hooks获取数据。但是,如果你对错误处理、加载提示、如何从表单中触发数据获取以及如何实现可重用的数据获取hook感兴趣,请继续阅读。如何通过编程方式/手动方式触发hook?好的,我们在mount后获取了一次数据,但是,如果使用input的字段来告诉API哪一个话题是我们感兴趣的呢?“Redux”可以作为我们的默认查询,如果是关于“React”的呢?让我们实现一个input元素,使某人能够获取“Redux”以外的话题。因此,为input元素引入一个新的状态。import React, { Fragment, useState, useEffect } from ‘react’;import axios from ‘axios’;function App() { const [data, setData] = useState({ hits: [] }); const [query, setQuery] = useState(‘redux’); useEffect(() => { const fetchData = async () => { const result = await axios( ‘http://hn.algolia.com/api/v1/search?query=redux', ); setData(result.data); }; fetchData(); }, []); return ( <Fragment> <input type=“text” value={query} onChange={event => setQuery(event.target.value)} /> <ul> {data.hits.map(item => ( <li key={item.objectID}> <a href={item.url}>{item.title}</a> </li> ))} </ul> </Fragment> );}export default App;目前,这两个状态彼此独立,但现在希望将它们耦合起来,以获取由input中的输入来查询指定的项目。通过下面的更改,组件应该在挂载之后通过查询词获取所有数据。function App() { const [data, setData] = useState({ hits: [] }); const [query, setQuery] = useState(‘redux’); useEffect(() => { const fetchData = async () => { const result = await axios( http://hn.algolia.com/api/v1/search?query=${query}, ); setData(result.data); }; fetchData(); }, []); return ( … );}export default App;还差一部分:当你尝试在input中输入一些内容时,在mount之后就不会再获取任何数据了,这是因为我们提供了空数组作为第二个参数,effect没有依赖任何变量,因此只会在mount的时候触发,但是现在的effect应该依赖query,每当query改变的时候,就应该触发数据的获取。function App() { const [data, setData] = useState({ hits: [] }); const [query, setQuery] = useState(‘redux’); useEffect(() => { const fetchData = async () => { const result = await axios( http://hn.algolia.com/api/v1/search?query=${query}, ); setData(result.data); }; fetchData(); }, [query]); return ( … );}export default App;现在每当input的值更新的时候就可以重新获取数据了。但这又导致了另一个问题:对于input中键入的每个字符,都会触发该效果,并执行一个数据提取请求。如何提供一个按钮来触发请求,从而手动hook呢?function App() { const [data, setData] = useState({ hits: [] }); const [query, setQuery] = useState(‘redux’); const [search, setSearch] = useState(’’); useEffect(() => { const fetchData = async () => { const result = await axios( http://hn.algolia.com/api/v1/search?query=${query}, ); setData(result.data); }; fetchData(); }, [query]); return ( <Fragment> <input type=“text” value={query} onChange={event => setQuery(event.target.value)} /> <button type=“button” onClick={() => setSearch(query)}> Search </button> <ul> {data.hits.map(item => ( <li key={item.objectID}> <a href={item.url}>{item.title}</a> </li> ))} </ul> </Fragment> );}现在,effect依赖于于search,而不是随输入字段中变化的query。一旦用户点击按钮,新的search就会被设置,并且应该手动触发effect hook。function App() { const [data, setData] = useState({ hits: [] }); const [query, setQuery] = useState(‘redux’); const [search, setSearch] = useState(‘redux’); useEffect(() => { const fetchData = async () => { const result = await axios( http://hn.algolia.com/api/v1/search?query=${search}, ); setData(result.data); }; fetchData(); }, [search]); return ( … );}export default App;此外,search的初始值也设置为与query相同,因为组件也在mount时获取数据,因此结果应反映输入字段中的值。但是,具有类似的query和search状态有点令人困惑。为什么不将实际的URL设置为状态而来代替search?function App() { const [data, setData] = useState({ hits: [] }); const [query, setQuery] = useState(‘redux’); const [url, setUrl] = useState( ‘http://hn.algolia.com/api/v1/search?query=redux', ); useEffect(() => { const fetchData = async () => { const result = await axios(url); setData(result.data); }; fetchData(); }, [url]); return ( <Fragment> <input type=“text” value={query} onChange={event => setQuery(event.target.value)} /> <button type=“button” onClick={() => setUrl(http://hn.algolia.com/api/v1/search?query=${query}) } > Search </button> <ul> {data.hits.map(item => ( <li key={item.objectID}> <a href={item.url}>{item.title}</a> </li> ))} </ul> </Fragment> );}这就是使用effect hook获取隐式编程数据的情况。你可以决定effect依赖于哪个状态。一旦在点击或其他effect中设置此状态,此effect将再次运行。在这种情况下,如果URL状态发生变化,effect将再次运行以从API获取数据。React Hooks和loading让我们为数据获取引入一个加载提示。它只是另一个由state hook管理的状态。loading被用于在组件中渲染一个loading提示。import React, { Fragment, useState, useEffect } from ‘react’;import axios from ‘axios’;function App() { const [data, setData] = useState({ hits: [] }); const [query, setQuery] = useState(‘redux’); const [url, setUrl] = useState( ‘http://hn.algolia.com/api/v1/search?query=redux', ); const [isLoading, setIsLoading] = useState(false); useEffect(() => { const fetchData = async () => { setIsLoading(true); const result = await axios(url); setData(result.data); setIsLoading(false); }; fetchData(); }, [url]); return ( <Fragment> <input type=“text” value={query} onChange={event => setQuery(event.target.value)} /> <button type=“button” onClick={() => setUrl(http://hn.algolia.com/api/v1/search?query=${query}) } > Search </button> {isLoading ? ( <div>Loading …</div> ) : ( <ul> {data.hits.map(item => ( <li key={item.objectID}> <a href={item.url}>{item.title}</a> </li> ))} </ul> )} </Fragment> );}export default App;一旦调用该effect进行数据获取(当组件mount或URL状态更改时发生),加载状态将设置为true。一旦请求完成,加载状态将再次设置为false。React Hooks和错误处理如果在React Hooks中加上错误处理呢,错误只是用state hook初始化的另一个状态。一旦出现错误状态,应用程序组件就可以为用户提供反馈。使用async/await时,通常使用try/catch块进行错误处理。你可以在effect内做到:import React, { Fragment, useState, useEffect } from ‘react’;import axios from ‘axios’;function App() { const [data, setData] = useState({ hits: [] }); const [query, setQuery] = useState(‘redux’); const [url, setUrl] = useState( ‘http://hn.algolia.com/api/v1/search?query=redux', ); const [isLoading, setIsLoading] = useState(false); const [isError, setIsError] = useState(false); useEffect(() => { const fetchData = async () => { setIsError(false); setIsLoading(true); try { const result = await axios(url); setData(result.data); } catch (error) { setIsError(true); } setIsLoading(false); }; fetchData(); }, [url]); return ( <Fragment> <input type=“text” value={query} onChange={event => setQuery(event.target.value)} /> <button type=“button” onClick={() => setUrl(http://hn.algolia.com/api/v1/search?query=${query}) } > Search </button> {isError && <div>Something went wrong …</div>} {isLoading ? ( <div>Loading …</div> ) : ( <ul> {data.hits.map(item => ( <li key={item.objectID}> <a href={item.url}>{item.title}</a> </li> ))} </ul> )} </Fragment> );}export default App;React在表单中获取数据到目前为止,我们只有input和按钮的组合。一旦引入更多的输入元素,您可能需要用一个表单元素包装它们。此外,表单还可以通过键盘上的“enter”来触发。function App() { … return ( <Fragment> <form onSubmit={() => setUrl(http://hn.algolia.com/api/v1/search?query=${query}) } > <input type=“text” value={query} onChange={event => setQuery(event.target.value)} /> <button type=“submit”>Search</button> </form> {isError && <div>Something went wrong …</div>} … </Fragment> );}但是现在浏览器在单击提交按钮时页面会重新加载,因为这是浏览器在提交表单时的固有行为。为了防止默认行为,我们可以通过event.preventDefault()取消默认行为。这也是在React类组件中实现的方法。function App() { … const doFetch = () => { setUrl(http://hn.algolia.com/api/v1/search?query=${query}); }; return ( <Fragment> <form onSubmit={event => { doFetch(); event.preventDefault(); }}> <input type=“text” value={query} onChange={event => setQuery(event.target.value)} /> <button type=“submit”>Search</button> </form> {isError && <div>Something went wrong …</div>} … </Fragment> );}现在,当你单击提交按钮时,浏览器不会再重新加载。它和以前一样工作,但这次使用的是表单,而不是简单的input和按钮组合。你也可以按键盘上的“回车”键。自定义数据获取hook为了提取用于数据获取的自定义hook,请将属于数据获取的所有内容,移动到一个自己的函数中。还要确保能够返回App组件所需要的全部变量。const useHackerNewsApi = () => { const [data, setData] = useState({ hits: [] }); const [url, setUrl] = useState( ‘http://hn.algolia.com/api/v1/search?query=redux', ); const [isLoading, setIsLoading] = useState(false); const [isError, setIsError] = useState(false); useEffect(() => { const fetchData = async () => { setIsError(false); setIsLoading(true); try { const result = await axios(url); setData(result.data); } catch (error) { setIsError(true); } setIsLoading(false); }; fetchData(); }, [url]); const doFetch = () => { setUrl(http://hn.algolia.com/api/v1/search?query=${query}); }; return { data, isLoading, isError, doFetch };}现在,你可以在App组件中使用新的hook了。function App() { const [query, setQuery] = useState(‘redux’); const { data, isLoading, isError, doFetch } = useHackerNewsApi(); return ( <Fragment> … </Fragment> );}接下来,从dofetch函数外部传递URL状态:const useHackerNewsApi = () => { … useEffect( … ); const doFetch = url => { setUrl(url); }; return { data, isLoading, isError, doFetch };};function App() { const [query, setQuery] = useState(‘redux’); const { data, isLoading, isError, doFetch } = useHackerNewsApi(); return ( <Fragment> <form onSubmit={event => { doFetch( http://hn.algolia.com/api/v1/search?query=${query}, ); event.preventDefault(); }} > <input type=“text” value={query} onChange={event => setQuery(event.target.value)} /> <button type=“submit”>Search</button> </form> … </Fragment> );}初始状态也可以变为通用状态。把它简单地传递给新的自定义hook:import React, { Fragment, useState, useEffect } from ‘react’;import axios from ‘axios’;const useDataApi = (initialUrl, initialData) => { const [data, setData] = useState(initialData); const [url, setUrl] = useState(initialUrl); const [isLoading, setIsLoading] = useState(false); const [isError, setIsError] = useState(false); useEffect(() => { const fetchData = async () => { setIsError(false); setIsLoading(true); try { const result = await axios(url); setData(result.data); } catch (error) { setIsError(true); } setIsLoading(false); }; fetchData(); }, [url]); const doFetch = url => { setUrl(url); }; return { data, isLoading, isError, doFetch };};function App() { const [query, setQuery] = useState(‘redux’); const { data, isLoading, isError, doFetch } = useDataApi( ‘http://hn.algolia.com/api/v1/search?query=redux', { hits: [] }, ); return ( <Fragment> <form onSubmit={event => { doFetch( http://hn.algolia.com/api/v1/search?query=${query}, ); event.preventDefault(); }} > <input type=“text” value={query} onChange={event => setQuery(event.target.value)} /> <button type=“submit”>Search</button> </form> {isError && <div>Something went wrong …</div>} {isLoading ? ( <div>Loading …</div> ) : ( <ul> {data.hits.map(item => ( <li key={item.objectID}> <a href={item.url}>{item.title}</a> </li> ))} </ul> )} </Fragment> );}export default App;这就是使用自定义hook获取数据的方法。hook本身对API一无所知。它从外部接收所有参数,只管理必要的状态,如数据、加载和错误状态。它执行请求并将数据作为自定义数据获取hook返回给组件。Reducer的数据获取hookreducer hook返回一个状态对象和一个改变状态对象的函数。dispatch函数接收type和可选的payload。所有这些信息都在实际的reducer函数中使用,从以前的状态、包含可选payload和type的action中提取新的状态。让我们看看这在代码中是如何工作的:import React, { Fragment, useState, useEffect, useReducer,} from ‘react’;import axios from ‘axios’;const dataFetchReducer = (state, action) => { …};const useDataApi = (initialUrl, initialData) => { const [url, setUrl] = useState(initialUrl); const [state, dispatch] = useReducer(dataFetchReducer, { isLoading: false, isError: false, data: initialData, }); …};Reducer Hook接受reducer函数和一个初始化的状态对象作为参数,在我们的例子中,数据、加载和错误状态的初始状态的参数没有改变,但是它们被聚合到由一个reducer hook管理的一个状态对象,而不是单个state hook。const dataFetchReducer = (state, action) => { …};const useDataApi = (initialUrl, initialData) => { const [url, setUrl] = useState(initialUrl); const [state, dispatch] = useReducer(dataFetchReducer, { isLoading: false, isError: false, data: initialData, }); useEffect(() => { const fetchData = async () => { dispatch({ type: ‘FETCH_INIT’ }); try { const result = await axios(url); dispatch({ type: ‘FETCH_SUCCESS’, payload: result.data }); } catch (error) { dispatch({ type: ‘FETCH_FAILURE’ }); } }; fetchData(); }, [url]); …};现在,在获取数据时,可以使用dispatch向reducer函数发送信息。dispatch函数发送的对象包括一个必填的type属性和可选的payload。type告诉Reducer函数需要应用哪个状态转换,并且Reducer还可以使用payload来提取新状态。毕竟,我们只有三种状态转换:初始化获取过程,通知成功的数据获取结果,以及通知错误的数据获取结果。在自定义hook的最后,状态像以前一样返回,但是因为我们有一个状态对象,而不再是独立状态,所以需要用扩展运算符返回state。这样,调用useDataApi自定义hook的用户仍然可以访问data、isloading和isError:const useDataApi = (initialUrl, initialData) => { const [url, setUrl] = useState(initialUrl); const [state, dispatch] = useReducer(dataFetchReducer, { isLoading: false, isError: false, data: initialData, }); … const doFetch = url => { setUrl(url); }; return { …state, doFetch };};最后,还缺少了reducer函数的实现。它需要处理三种不同的状态转换,即FETCH_INIT、FETCH_SUCCESS和FETCH_FAILURE。每个状态转换都需要返回一个新的状态对象。让我们看看如何用switch case语句实现这一点:const dataFetchReducer = (state, action) => { switch (action.type) { case ‘FETCH_INIT’: return { …state }; case ‘FETCH_SUCCESS’: return { …state }; case ‘FETCH_FAILURE’: return { …state }; default: throw new Error(); }};reducer函数可以通过其参数访问当前状态和action。到目前为止,switch case语句中的每个状态转换只会返回原来的状态。…语句用于保持状态对象不变(意味着状态永远不会直接改变),现在,让我们重写一些当前状态返回的属性,以便在每次状态转换时更改状态:const dataFetchReducer = (state, action) => { switch (action.type) { case ‘FETCH_INIT’: return { …state, isLoading: true, isError: false }; case ‘FETCH_SUCCESS’: return { …state, isLoading: false, isError: false, data: action.payload, }; case ‘FETCH_FAILURE’: return { …state, isLoading: false, isError: true, }; default: throw new Error(); }};现在,每个状态转换(由操作的type决定)都将基于先前的状态和可选的payload返回一个新的状态。例如,在成功请求的情况下,payload用于设置新状态对象的数据。总之,reducer hook确保状态管理的这一部分是用自己的逻辑封装的。通过提供type和可选payload,你将始终已一个可预测的状态结束。此外,你将永远不会进入无效状态。例如,以前可能会意外地将isloading和isError状态设置为true。在这个案例的用户界面中应该显示什么?现在,reducer函数定义的每个状态转换都会导致一个有效的状态对象。在effect hook中禁止数据获取即使组件已经卸载(例如,由于使用react路由器导航而离开),设置组件状态也是react中的一个常见问题。我以前在这里写过这个问题,它描述了如何防止在各种场景中为unmount的组件设置状态。让我们看看如何防止在自定义hook中为数据获取设置状态:const useDataApi = (initialUrl, initialData) => { const [url, setUrl] = useState(initialUrl); const [state, dispatch] = useReducer(dataFetchReducer, { isLoading: false, isError: false, data: initialData, }); useEffect(() => { let didCancel = false; const fetchData = async () => { dispatch({ type: ‘FETCH_INIT’ }); try { const result = await axios(url); if (!didCancel) { dispatch({ type: ‘FETCH_SUCCESS’, payload: result.data }); } } catch (error) { if (!didCancel) { dispatch({ type: ‘FETCH_FAILURE’ }); } } }; fetchData(); return () => { didCancel = true; }; }, [url]); const doFetch = url => { setUrl(url); }; return { …state, doFetch };};每个effect hook都有一个clean功能,在组件卸载时运行。clean函数是从hook返回的一个函数。在我们的例子中,我们使用一个名为didCancel的布尔标志,让我们的数据获取逻辑知道组件的状态(已装载/未装载)。如果组件已卸载,则标志应设置为“tree”,这将导致在最终异步解决数据提取后无法设置组件状态。注意:事实上,数据获取不会中止——这可以通过axios的Cancellation实现——但是对于未安装的组件,状态转换会不再执行。因为在我看来,axios的Cancellation并不是最好的API,所以这个防止设置状态的布尔标志也能起到作用。你已经了解了在React中state和effect hook如何用于获取数据。如果您对使用render props和高阶组件在类组件(和函数组件)中获取数据很感兴趣,请从一开始就去我的另一篇文章。否则,我希望本文对您了解react hook以及如何在现实场景中使用它们非常有用。 ...

April 1, 2019 · 8 min · jiezi

ApacheCN 翻译活动进度公告 2019.3.31

我们是一个大型开源社区,旗下 QQ 群共 9000 余人,Github Star 数量超过 20k 个,网站日 uip 超过 4k,拥有 CSDN 博客专家和简书程序员优秀作者认证。我们组织公益性的翻译活动、学习活动和比赛组队活动,并和 DataWhale、LinuxStory 等国内著名开源组织保持良好的合作关系。与商业组织不同,我们并不会追逐热点,或者唯利是图。作为公益组织,我们将完成项目放在首要位置,并有足够时间把项目打磨到极致。我们希望做出广大 AI 爱好者真正需要的东西,打造真正有价值的长尾作品。【主页】 apachecn.org【Github】@ApacheCN暂时下线: 社区暂时下线: cwiki 知识库自媒体平台微博:@ApacheCN知乎:@ApacheCNCSDN | 简书 | OSChina | 博客园头条号 | 搜狐号 | bilibili 专栏We are ApacheCN Open Source Organization, not ASF! We are fans of AI, and have no relationship with ASF!合作or侵权,请联系【fonttian】<fonttian@gmail.com> | 请抄送一份到 <apachecn@163.com>seaborn 0.9 中文文档参与方式:https://github.com/apachecn/s…整体进度:https://github.com/apachecn/s…项目仓库:https://github.com/apachecn/s…认领:31/74,翻译:5/74序号章节译者进度1An introduction to seaborn@yiran7324100%2Installing and getting started@neolei100%3Visualizing statistical relationships@JNJYan100%4Plotting with categorical data@hold2010 5Visualizing the distribution of a dataset@alohahahaha100%6Visualizing linear relationships@friedhelm739 7Building structured multi-plot grids@keyianpai 8Controlling figure aesthetics 9Choosing color palettes@Modrisco100%10seaborn.relplot 11seaborn.scatterplot 12seaborn.lineplot 13seaborn.catplot 14seaborn.stripplot 15seaborn.swarmplot 16seaborn.boxplot 17seaborn.violinplot 18seaborn.boxenplot 19seaborn.pointplot 20seaborn.barplot@melon-bun 21seaborn.countplot 22seaborn.jointplot 23seaborn.pairplot 24seaborn.distplot 25seaborn.kdeplot 26seaborn.rugplot 27seaborn.lmplot 28seaborn.regplot 29seaborn.residplot 30seaborn.heatmap 31seaborn.clustermap 32seaborn.FacetGrid 33seaborn.FacetGrid.map 34seaborn.FacetGrid.map_dataframe 35seaborn.PairGrid 36seaborn.PairGrid.map 37seaborn.PairGrid.map_diag 38seaborn.PairGrid.map_offdiag 39seaborn.PairGrid.map_lower 40seaborn.PairGrid.map_upper 41seaborn.JointGrid 42seaborn.JointGrid.plot 43seaborn.JointGrid.plot_joint 44seaborn.JointGrid.plot_marginals 45seaborn.set 46seaborn.axes_style 47seaborn.set_style 48seaborn.plotting_context 49seaborn.set_context 50seaborn.set_color_codes 51seaborn.reset_defaults 52seaborn.reset_orig 53seaborn.set_palette@Modrisco 54seaborn.color_palette@Modrisco 55seaborn.husl_palette@Modrisco 56seaborn.hls_palette@Modrisco 57seaborn.cubehelix_palette@Modrisco 58seaborn.dark_palette@Modrisco 59seaborn.light_palette@Modrisco 60seaborn.diverging_palette@Modrisco 61seaborn.blend_palette@Modrisco 62seaborn.xkcd_palette@Modrisco 63seaborn.crayon_palette@Modrisco 64seaborn.mpl_palette@Modrisco 65seaborn.choose_colorbrewer_palette@Modrisco 66seaborn.choose_cubehelix_palette@Modrisco 67seaborn.choose_light_palette@Modrisco 68seaborn.choose_dark_palette@Modrisco 69seaborn.choose_diverging_palette@Modrisco 70seaborn.load_dataset@Modrisco 71seaborn.despine@Modrisco 72seaborn.desaturate@Modrisco 73seaborn.saturate@Modrisco 74seaborn.set_hls_values@Modrisco Git 中文参考(校对)参与方式:https://github.com/apachecn/g…整体进度:https://github.com/apachecn/g…项目仓库:https://github.com/apachecn/g…认领:0/83,校对:0/83序号章节贡献者进度1git 2git-config 3git-help 4git-init 5git-clone 6git-add 7git-status 8git-diff 9git-commit 10git-reset 11git-rm 12git-mv 13git-branch 14git-checkout 15git-merge 16git-mergetool 17git-log 18git-stash 19git-tag 20git-worktree 21git-fetch 22git-pull 23git-push 24git-remote 25git-submodule 26git-show 27git-log 29git-shortlog 30git-describe 31git-apply 32git-cherry-pick 34git-rebase 35git-revert 36git-bisect 37git-blame 38git-grep 39gitattributes 40giteveryday 41gitglossary 42githooks 43gitignore 44gitmodules 45gitrevisions 46gittutorial 47gitworkflows 48git-am 50git-format-patch 51git-send-email 52git-request-pull 53git-svn 54git-fast-import 55git-clean 56git-gc 57git-fsck 58git-reflog 59git-filter-branch 60git-instaweb 61git-archive 62git-bundle 63git-daemon 64git-update-server-info 65git-cat-file 66git-check-ignore 67git-checkout-index 68git-commit-tree 69git-count-objects 70git-diff-index 71git-for-each-ref 72git-hash-object 73git-ls-files 74git-merge-base 75git-read-tree 76git-rev-list 77git-rev-parse 78git-show-ref 79git-symbolic-ref 80git-update-index 81git-update-ref 82git-verify-pack 83git-write-tree HBase 3.0 中文参考指南(校对)参与方式:https://github.com/apachecn/h…整体进度:https://github.com/apachecn/h…项目仓库:https://github.com/apachecn/h…认领:18/31,校对:11/31章节贡献者进度Preface@xixici100%Getting Started@xixici100%Apache HBase Configuration@xixici100%Upgrading@xixici100%The Apache HBase Shell@xixici100%Data Model@Winchester-Yi HBase and Schema Design@RaymondCode100%RegionServer Sizing Rules of Thumb HBase and MapReduce@BridgetLai100%Securing Apache HBase Architecture@RaymondCode In-memory Compaction@mychaow Backup and Restore@mychaow Synchronous Replication@mychaow Apache HBase APIs@xixici100%Apache HBase External APIs@xixici100%Thrift API and Filter Language@xixici100%HBase and Spark@TsingJyujing100%Apache HBase Coprocessors@TsingJyujing Apache HBase Performance Tuning Troubleshooting and Debugging Apache HBase Apache HBase Case Studies Apache HBase Operational Management Building and Developing Apache HBase Unit Testing HBase Applications Protobuf in HBase@TsingJyujing Procedure Framework (Pv2): HBASE-12439 AMv2 Description for Devs ZooKeeper Community Appendix UCB CS61b:Java 中的数据结构参与方式:https://github.com/apachecn/c…整体进度:https://github.com/apachecn/c…项目仓库:https://github.com/apachecn/c…认领:5/12,翻译:4/12标题译者进度一、算法复杂度@leader402二、抽象数据类型@Allenyep100%三、满足规范 四、序列和它们的实现@biubiubiuboomboomboom100%五、树@biubiubiuboomboomboom100%六、搜索树 七、哈希 八、排序和选择@Rachel-Hu100%九、平衡搜索 十、并发和同步 十一、伪随机序列 十二、图 UCB Prob140:面向数据科学的概率论参与方式:https://github.com/apachecn/p…整体进度:https://github.com/apachecn/p…项目仓库:https://github.com/apachecn/p…认领:22/25,翻译:19/25标题译者翻译进度一、基础飞龙100%二、计算几率飞龙100%三、随机变量飞龙100%四、事件之间的关系@biubiubiuboomboomboom100%五、事件集合 >0%六、随机计数@viviwong100%七、泊松化@YAOYI626100%八、期望 50%九、条件(续)@YAOYI626100%十、马尔科夫链喵十八100%十一、马尔科夫链(续)喵十八100%十二、标准差缺只萨摩100%十三、方差和协方差缺只萨摩100%十四、中心极限定理喵十八100%十五、连续分布@ThunderboltSmile十六、变换@hellozhaihy十七、联合密度@Winchester-Yi100%十八、正态和 Gamma 族@Winchester-Yi100%十九、和的分布平淡的天100%二十、估计方法平淡的天100%二十一、Beta 和二项@lvzhetx100%二十二、预测 50%二十三、联合正态随机变量@JUNE951234二十四、简单线性回归@ThomasCai100%二十五、多元回归@lanhaixuan100%认领完毕OpenCV 4.0 中文教程参与方式:https://github.com/apachecn/o…整体进度:https://github.com/apachecn/o…项目仓库:https://github.com/apachecn/o…认领:51/51,翻译:24/51。Pytorch 1.0 中文文档参与方式:https://github.com/apachecn/p…整体进度:https://github.com/apachecn/p…项目仓库:https://github.com/apachecn/p…教程部分:认领:37/37,翻译:34/37;文档部分:认领:39/39,翻译:34/39Airflow 中文文档(校对)参与方式:https://github.com/apachecn/a…整体进度:https://github.com/apachecn/a…项目仓库:https://github.com/apachecn/a…认领:29/30,校对:27/30翻译征集要求:机器学习/数据科学相关或者编程相关原文必须在互联网上开放不能只提供 PDF 格式(我们实在不想把精力都花在排版上)请先搜索有没有人翻译过请回复本文。赞助我们 ...

March 31, 2019 · 2 min · jiezi

Machine Learning Mastery 博客文章翻译:XGBoost

请您勇敢地去翻译和改进翻译。虽然我们追求卓越,但我们并不要求您做到十全十美,因此请不要担心因为翻译上犯错——在大部分情况下,我们的服务器已经记录所有的翻译,因此您不必担心会因为您的失误遭到无法挽回的破坏。(改编自维基百科)通过在 Python 中使用 XGBoost 提前停止来避免过度拟合如何在 Python 中调优 XGBoost 的多线程支持如何配置梯度提升算法在 Python 中使用 XGBoost 进行梯度提升的数据准备如何使用 scikit-learn 在 Python 中开发您的第一个 XGBoost 模型如何在 Python 中使用 XGBoost 评估梯度提升模型在 Python 中使用 XGBoost 的特征重要性和特征选择浅谈机器学习的梯度提升算法应用机器学习的 XGBoost 简介如何在 macOS 上为 Python 安装 XGBoost如何在 Python 中使用 XGBoost 保存梯度提升模型从梯度提升开始,比较 165 个数据集上的 13 种算法在 Python 中使用 XGBoost 和 scikit-learn 进行随机梯度提升如何使用 Amazon Web Services 在云中训练 XGBoost 模型在 Python 中使用 XGBoost 调整梯度提升的学习率如何在 Python 中使用 XGBoost 调整决策树的数量和大小如何在 Python 中使用 XGBoost 可视化梯度提升决策树在 Python 中开始使用 XGBoost 的 7 步迷你课程 ...

March 26, 2019 · 1 min · jiezi

【译】函数组件和类组件有什么不同?

原文链接:https://overreacted.io/how-ar…在很长一段时间内,标准答案是class components提供更多的特性(像state)。但随着Hooks的出现,答案就不再是这样子了。或许你听说过他们中的一个性能可能更好,哪一个?因为各种的判断标准获取都存在缺陷,所以我们需要小心仔细的得出结论。性能的好坏主要取决于什么?它主要取决于你的代码在做什么,而不是你使用的是function还是class。在我们的观察中,尽管优化的策略可能会有些许的不同,但性能的差异几乎可以忽略不及。无论是哪种情况,我们都不建议你重写现有的组件,除非你有一些其他的原因或者是想成为Hooks的早期的采用者。Hooks仍然是一个新特性(就像2014年的React一样),一些最佳实践还没有被写入到教程中。那我们该怎么办?function components和class components之间有什么本质的区别吗?显然,在构思模型中是不同的。在这篇文章中,我们将看到他们之间最大的区别,自从2015年推出function componetns以来,它就一直存在着,但是却经常被忽视:function components捕获渲染值(capture value)注意: 本文并不是对函数或者类的值做判断。我只描述了React中这两个编程模型之间的区别。有关更广泛地采用函数的问题,请参阅hooks常见问题解答。思考下面这样一个组件:function ProfilePage(props) { const showMessage = () => { alert(‘Followed ’ + props.user); }; const handleClick = () => { setTimeout(showMessage, 3000); }; return ( <button onClick={handleClick}>Follow</button> );}这个组件通过setTimeout模拟网络请求,在点击按钮3秒后弹出props.user的值,如果props.user的值是Dan的话,他将在点击后3秒弹出“Followed Dan”。(注意,使用箭头函数还是函数声明的形式并不重要,handleClick函数的工作方式完全相同)如果改写成class形式可能长下面这个样子:class ProfilePage extends React.Component { showMessage = () => { alert(‘Followed ’ + this.props.user); }; handleClick = () => { setTimeout(this.showMessage, 3000); }; render() { return <button onClick={this.handleClick}>Follow</button>; }}通常认为这两段代码是等效的。但是大家经常在这两种形式之间来回切换代码而不去关注他们的含义。然而其实这两段代码是有细微的差别的,就我个人而言,我花费了一段时间才看出来。如果你自己想弄清楚的话,这里有一个在线demo。本文的剩余部分解释了有什么不同和它的重要性。再继续之前,我要强调,我所描述的差异本身和React Hooks无关,上面的例子甚至都没有使用Hooks。这全部都是React中,function和class的差别。如果你想要在React应用中去频繁的使用function components,那么你应该去了结它。我们将用一个在React应用程序中常见的错误来说明这一区别。打开这个示例,有一个主页select和两个主页,且每一个包含一个Follow按钮。尝试按照下面的顺序操作:点击一个Follow按钮改变select选项然后等待3秒查看alert的文字你会发现一个问题:在function components中,在Dan的主页点击follow然后切换到Sophie,alert仍然会展示“Followed Dan”。在class components中,alert的却是“Followed Sophie”。在这个例子中,第一个行为是正确的。如果我Follow A,然后导航B的主页,我的组件不应该Follow到B。这个class显然有缺陷。所以为什么我们class的例子展示出这样的结果呢?让我们仔细研究一下class中的showMessage方法:showMessage = () => { alert(‘Followed ’ + this.props.user);};这个方法从this.props.user取值,在React中,props应该是不可变的,但是this却是可变的。实际上,在React内部会随着时间的推移改变this,以便可以在render和生命周期中取到最新的版本。所以如果我们的组件在请求过程中re-render,this.props将会改变,showMessage方法将会从“最新”的props中取到user的值。这就暴露了一个关于UI的有趣问题。如果说UI是一个关于当前应用state的函数,那么事件处理函数就是render的一部分,就像是可视化输出一样。我们的事件处理函数“属于“某一特定state和props的render。但是在包含超时操作的回调函数内读取this.props会破坏这个关联。showMessage没有“绑定”到任何一个特定的render,因此它“丢失”了正确的props。我们说function components不会存在这个问题。那么我们该怎么去解决呢?我们需要去用某种方式“修复”正确的props到showMessage之间的关联。在执行的某个地方,props丢失了。一个简单的方式就是在早期我们就拿到这个this.props的值,然后显示的去将它传递到超时处理函数中:class ProfilePage extends React.Component { showMessage = (user) => { alert(‘Followed ’ + user); }; handleClick = () => { const {user} = this.props; setTimeout(() => this.showMessage(user), 3000); }; render() { return <button onClick={this.handleClick}>Follow</button>; }}这是可行的。然而,这种方法使代码更加冗长,并且随着时间的推移更容易出错。如果我们需要的不仅仅是一个单一的props怎么办?如果ShowMessage调用另一个方法,而该方法读取this.props.something或this.state.something,我们将再次遇到完全相同的问题。所以我们必须通过在ShowMessage调用的每个方法,将this.props和this.state作为参数传递。这样做我们通常会破坏一个class,并且会导致很多bug出现。同样,在handleClick中用alert展示也不能暴露出更深的问题。如果我们想要去结构化我们的代码,将代码拆分出不同的方法,并且在读取props和state时也能保持一样的展示结果,而且不仅仅在React中,你也可以在任何UI库中去调用它。也许,我们可以在构造函数中绑定这些方法?class ProfilePage extends React.Component { constructor(props) { super(props); this.showMessage = this.showMessage.bind(this); this.handleClick = this.handleClick.bind(this); } showMessage() { alert(‘Followed ’ + this.props.user); } handleClick() { setTimeout(this.showMessage, 3000); } render() { return <button onClick={this.handleClick}>Follow</button>; }}不,这并不能解决任何问题。记住,我们的问题是拿到this.props太晚了,而不是我们使用何种语法。但是,如果我们完全依赖于js的闭包,问题就会得到解决。闭包通常是被避免的,因为它很难考虑一个随时间变化的值。但是在React中,props和state应该是不可变的。这意味着,如果去掉某个特定render中的props或state,则始终可以指望它们保持相同:class ProfilePage extends React.Component { render() { // Capture the props! const props = this.props; // Note: we are inside render. // These aren’t class methods. const showMessage = () => { alert(‘Followed ’ + props.user); }; const handleClick = () => { setTimeout(showMessage, 3000); }; return <button onClick={handleClick}>Follow</button>; }}已经在render时“捕获”到了props。这样,它里面的任何代码(包括showMessage)都可以保证取到某个特定render中的props了。然后我们可以添加很多的helper函数,他们都可以捕获到props和state。闭包救了我们。上面的例子是正确的,但看起来很奇怪。如果只是在render中定义函数而不是使用类方法,那么我们使用一个class又有什么意义呢?实际上我们可以通过移除class来简化代码:function ProfilePage(props) { const showMessage = () => { alert(‘Followed ’ + props.user); }; const handleClick = () => { setTimeout(showMessage, 3000); }; return ( <button onClick={handleClick}>Follow</button> );}就像上面所说的,props仍然可以被捕获到,React将它作为一个参数传递。不同的是,props对象本身不会因React而发生变化了。在下面中就更明显了:function ProfilePage({ user }) { const showMessage = () => { alert(‘Followed ’ + user); }; const handleClick = () => { setTimeout(showMessage, 3000); }; return ( <button onClick={handleClick}>Follow</button> );}当父组件根据不同的props渲染时,React将会再次调用function,但是我们点击的事件处理函数是上一个包含user值的render,并且showMessage函数已经拿到了user这个值这就是为什么在这个版本的function demo中在Sophie主页点击Follow,然后改变select,将会alert “Followed Sophie”。现在我们知道了在React中 function 和 class的最大不同。function components捕获渲染值(capture value)对于钩子,同样的原理也适用于state。考虑这个例子:function MessageThread() { const [message, setMessage] = useState(’’); const showMessage = () => { alert(‘You said: ’ + message); }; const handleSendClick = () => { setTimeout(showMessage, 3000); }; const handleMessageChange = (e) => { setMessage(e.target.value); }; return ( <> <input value={message} onChange={handleMessageChange} /> <button onClick={handleSendClick}>Send</button> </> );}这里是在线demo这个例子说明了相同点:在点击send按钮后,再次修改输入框的值,3秒后的输出依然是点击前输入框的值。这说明function Hooks同样具有capture value的特性。所以我们知道了在React中function默认情况下会捕获props和state(capture value)。但是如果我们想要去避免这个capture value呢?在class中,我们可以通过使用this.props和this.state,因为this本事是可变的,React改变了它,在function components中,还有一个被所有组件所共享的可变值,被叫做ref:function MyComponent() { const ref = useRef(null); // You can read or write ref.current. // …}但是,你必须自己管理它。ref和实例字段有着相同的作用,你也许更为熟悉“dom refs”,但是这个概念更为普遍,它仅仅是一个“放置一些东西的通用容器”。尽管看起来它像是某一些东西的镜像,但实际上他们表示着相同的概念。React默认不会为function components创建保存最新props和state的refs。因为很多情况下你是不需要他们的,并且分配他们也很浪费时间。但是需要的时候可以手动的去跟踪值:function MessageThread() { const [message, setMessage] = useState(’’); const latestMessage = useRef(’’); const showMessage = () => { alert(‘You said: ’ + latestMessage.current); }; const handleSendClick = () => { setTimeout(showMessage, 3000); }; const handleMessageChange = (e) => { setMessage(e.target.value); latestMessage.current = e.target.value; };这时我们发现,在点击send按钮后继续输入,3秒后alert的是点击按钮后输入的值而不是点击按钮钱输入的值。通常,应该避免在渲染期间读取或设置refs,因为它们是可变的。我们希望保持渲染的可预测性。但是,如果我们想要获取特定props或state的最新值,手动更新ref可能会很烦人。我们可以通过使用一种效果来实现自动化(useEffect在每次render都会执行):function MessageThread() { const [message, setMessage] = useState(’’); // Keep track of the latest value. const latestMessage = useRef(’’); useEffect(() => { latestMessage.current = message; }); const showMessage = () => { alert(‘You said: ’ + latestMessage.current); };这里是demo我们在effect中进行赋值,以便ref的值只在DOM更新后才更改。像这样使用ref不是经常需要的。通常capture props或state才是默认更好的选择。但是,在处理诸如间隔和订阅之类的命令式API时,它非常方便。记住,您可以跟踪任何这样的值:一个prop、一个state变量、整个props对象,甚至一个函数。在本文中,我们研究了class中常见的中断模式,以及闭包如何帮助我们修复它。但是,你可能已经注意到,当你试图通过指定依赖数组来优化Hooks时,可能会遇到带有过时闭包的错误。这是否意味着闭包是问题所在?我不这么认为。正如我们上面所看到的,闭包实际上帮助我们解决了难以注意到的细微问题。类似地,它们使在并发模式下正确地编写代码变得更加容易。到目前为止,我所看到的所有情况下,“过时的闭包”问题都是由于错误地假设“函数不更改”或“props总是相同”而发生的。事实并非如此,我希望这篇文章能够帮助澄清。function components没有props和state,因此它们的也同样重要。这不是bug,而是function components的一个特性。例如,函数不应该从useEffect或useCallback的“依赖项数组”中被排除。(正确的解决方案通常是上面的useReducer或useRef解决方案。)当我们用函数编写大多数React代码时,我们需要调整优化代码的直觉,以及什么值会随着时间而改变。正如Fredrik所说:对于Hooks,我迄今为止发现的最好的规则是“代码就像任何值在任何时候都可以改变”。React的function总是捕捉它们的值(capture value)—— 现在我们知道为什么了。 ...

March 26, 2019 · 2 min · jiezi

翻译[RFC6238] TOTP: Time-Based One-Time Password Algorithm

在闲暇时间做了一个TOTP相关的开源项目,在项目初步完成之余,我尝试对[RFC6238]文档进行了翻译,供大家参考与查阅,若有不妥之处,还望各位前辈海涵斧正。[RFC6238] : Time-Based One-Time Password Algorithm文章概要这篇文档主要讲述了关于一次性密码(OTP)的一个扩展算法,此算法是在,RFC4226文档中定义的’基于HMAC的一次性密码算法’基础之上,支持了基于时间移动因子的扩展算法。This document describes an extension of the One-Time Password (OTP) algorithm, namely the HMAC-based One-Time Password (HOTP) algorithm, as defined in RFC 4226, to support the time-based moving factor.HOTP算法是一个以事件计数器作为移动因子,基于事件的一次性密码算法。The HOTP algorithm specifies an event-based OTP algorithm, where the moving factor is an event counter.本文所讲述的算法则是将时间值作为移动因子。The present work bases the moving factor on a time value. 这个基于时间的一次性密码生成算法提供了有效时间更短的一次性密码,增强了OTP算法的安全性。 A time-based variant of the OTP algorithm provides short-lived OTP values, which are desirable for enhanced security.此算法可以广泛的应用于互联网应用之中,包括远程虚拟专用网络(VPN)的访问控制,Wi-Fi网络登录以及面向交易的网络应用等等。The proposed algorithm can be used across a wide range of network applications, from remote Virtual Private Network (VPN) access and Wi-Fi network logon to transaction-oriented Web applications. 作者相信通过商用和开源的算法实现,一个通用的共享算法将会促进互联网上更多的人接触并使用到双因素身份认证算法。The authors believe that a common and shared algorithm will facilitate adoption of two-factor authentication on the Internet by enabling interoperability across commercial and open-source implementations. ...

March 26, 2019 · 1 min · jiezi

odoo 国际化翻译

翻译功能简述每个模块的翻译文件放在该模块目录下i18n目录里。模块内相关字符串一般用英语写成,然后通过翻译模板导出功能,导出一个翻译模板po文件。翻译人员使用翻译软件(poedit)进行翻译后,产生对应语言po文件,再放入i18n目录下供odoo加载。po文件的文件名规则一般由对应语言缩写或语言_国家组成,如中文为zh.po或zh_CN.po这一步实现很简单,只需要在odoo中实现翻译成中文即可在已有的模块中要把因为页面翻译为中文页面,如下打开开发者模式点击设置在菜单中找到翻译,点击选择导出翻译语言选择“简体中文”文件格式选择po文件要导出的应用就是选择你要翻译的模块点击导出并下载po文件,第一步就完成了在你要翻译的模块下新建一个问价夹,文件命名为“i18n”重启你应用并更新就可以查看效果咯

March 14, 2019 · 1 min · jiezi

seaborn 0.9 文档翻译活动期待大家的参与~

参与方式:https://github.com/apachecn/s…整体进度:https://github.com/apachecn/s…项目仓库:https://github.com/apachecn/s…贡献指南请您勇敢地去翻译和改进翻译。虽然我们追求卓越,但我们并不要求您做到十全十美,因此请不要担心因为翻译上犯错——在大部分情况下,我们的服务器已经记录所有的翻译,因此您不必担心会因为您的失误遭到无法挽回的破坏。(改编自维基百科)负责人:飞龙:562826179章节列表An introduction to seabornInstalling and getting startedVisualizing statistical relationshipsPlotting with categorical dataVisualizing the distribution of a datasetVisualizing linear relationshipsBuilding structured multi-plot gridsControlling figure aestheticsChoosing color palettesseaborn.relplotseaborn.scatterplotseaborn.lineplotseaborn.catplotseaborn.stripplotseaborn.swarmplotseaborn.boxplotseaborn.violinplotseaborn.boxenplotseaborn.pointplotseaborn.barplotseaborn.countplotseaborn.jointplotseaborn.pairplotseaborn.distplotseaborn.kdeplotseaborn.rugplotseaborn.lmplotseaborn.regplotseaborn.residplotseaborn.heatmapseaborn.clustermapseaborn.FacetGridseaborn.FacetGrid.mapseaborn.FacetGrid.map_dataframeseaborn.PairGridseaborn.PairGrid.mapseaborn.PairGrid.map_diagseaborn.PairGrid.map_offdiagseaborn.PairGrid.map_lowerseaborn.PairGrid.map_upperseaborn.JointGridseaborn.JointGrid.plotseaborn.JointGrid.plot_jointseaborn.JointGrid.plot_marginalsseaborn.setseaborn.axes_styleseaborn.set_styleseaborn.plotting_contextseaborn.set_contextseaborn.set_color_codesseaborn.reset_defaultsseaborn.reset_origseaborn.set_paletteseaborn.color_paletteseaborn.husl_paletteseaborn.hls_paletteseaborn.cubehelix_paletteseaborn.dark_paletteseaborn.light_paletteseaborn.diverging_paletteseaborn.blend_paletteseaborn.xkcd_paletteseaborn.crayon_paletteseaborn.mpl_paletteseaborn.choose_colorbrewer_paletteseaborn.choose_cubehelix_paletteseaborn.choose_light_paletteseaborn.choose_dark_paletteseaborn.choose_diverging_paletteseaborn.load_datasetseaborn.despineseaborn.desaturateseaborn.saturateseaborn.set_hls_values流程一、认领首先查看整体进度,确认没有人认领了你想认领的章节。然后回复 ISSUE,注明“章节 + QQ 号”(一定要留 QQ)。二、翻译可以合理利用翻译引擎(例如谷歌),但一定要把它变得可读!如果遇到格式问题,请随手把它改正。三、提交fork Github 项目将译文放在docs文件夹下pushpull request请见 Github 入门指南。

March 14, 2019 · 1 min · jiezi

翻译: Spring Cloud Feign使用文档

转载请注明出处: 翻译: Spring Cloud Feign使用文档Why Feign and not X?Feign使用诸如Jersey和CXF之类的工具来实现ReST或SOAP服务的java客户端, 此外, Feign允许你在http库(如: Apache HC)之上编写自己的代码. 通过自定义解码器(decoders)和错误处理(error handing), Feign可以用最小的开销和最少的代码将你的代码关联到任何基于文本的http接口(http APIS),How does Feign work?Feign是通过将注解(annotations)转换成模板请求来实现它的功能的, Feign可以将请求参数直接应用到这些模板上. 尽管Feign只支持基于文本的接口, 但同样的它能显著地简化系统的方方面面, 如请求重放等, 此外, Feign也可以使你的单元测试更加简单.Java Version CampatibilityFeign 10.x及以上的版本是基于Java 8构建的, 且应该同样支持Java 9、10、11, 如果你需要在JDK 6的版本上使用的话, 请使用Feign 9.x版本.Basics下面的代码是适配Retrofit示例的用法:interface GitHub { @RequestLine(“GET /repos/{owner}/{repo}/contributors”) List<Contributor> contributors(@Param(“owner”) String owner, @Param(“repo”) String repo);}public static class Contributor { String login; int contributions;}public class MyApp { public static void main(String… args) { GitHub github = Feign.builder() .decoder(new GsonDecoder()) .target(GitHub.class, “https://api.github.com”); // Fetch and print a list of the contributors to this library. List<Contributor> contributors = github.contributors(“OpenFeign”, “feign”); for (Contributor contributor : contributors) { System.out.println(contributor.login + " (" + contributor.contributions + “)”); } }}Interface AnnotationsFeign的注解定义了接口与底层http客户端功能之间的约定, 默认情况下各个注解的约定含义如下:AnnotationInterface TargetUsage@RequestLine接口定义请求的HttpMethod和UriTemplate. 模板中可以使用大括号包围的表达式({expression}), 表达式的值由@Param对应参数的注解值提供.@Param参数定义模板变量, 变量的值应该由名字相对应的表达式提供.@Headers方法、Type定义HeaderTemplate; 使用@Param注解的值解析对应的表达式. 当该注解应用在Type上时, 该模板会被应用到每一个请求上. 当该注解应用在方法上时, 该模板仅会被应用到对应的方法上.@QueryMap参数将键值对类型的Map、POJO展开成地址上的请求参数(query string)@HeaderMap参数将键值对类型的Map展开成请求头Http Headers.@Body方法定义与UriTemplate和HeaderTemplate类似的模板(Template), 该模板可以使用@Param的注解值解析对应的表达式Templates and ExpressionsFeign支持由URI Template - RFC 6570定义的简单字符串(Level 1)表达式, 表达式的值从相关方法上对应@Param注解提供, 示例如下:public interface GitHub { @RequestLine(“GET /repos/{owner}/{repo}/contributors”) List<Contributor> getContributors(@Param(“owner”) String owner, @Param(“repo”) String repository); class Contributor { String login; int contributions; }}public class MyApp { public static void main(String[] args) { GitHub github = Feign.builder() .decoder(new GsonDecoder()) .target(GitHub.class, “https://api.github.com”); /* The owner and repository parameters will be used to expand the owner and repo expressions * defined in the RequestLine. * * the resulting uri will be https://api.github.com/repos/OpenFeign/feign/contributors / github.contributors(“OpenFeign”, “feign”); }}表达式必须使用大括号({})包裹着, 并且支持使用冒号(:)分隔的正则表达式来限定表达式的值. 如限定上述例子的owner参数的值必须是字母: {owner:[a-zA-Z]}.Request Parameter ExpansionRequestLine和QueryMap遵循 URI Template - RFC 6570规范对一级模板(Level 1 templates)的规定:未被解析的值将会被忽略.所有未编码或者通过@Param注解标记为已编码(encoded)的字符和变量值都使用pct编码(pct-encoded).可以从Advanced Usage一节查看更多示例.What about slashes? /默认情况下, @RequestLine和@QueryMap模板不会对正斜杠/进行编码, 如果需要默认对其进行编码的话, 可以将@RequestLine的decodeSlash属性值设置为false.What about plus? +根据URI规范, +可以使用在URI地址和请求参数(query segments)这两个部分上, 然而在请求参数(query)上对该符号的处理却有可能不一致, 在一些遗留的系统上, +会被解析成一个空白符(space). 对此, Feign采用现代系统对+的解释, 不会将+认为是一个空白符(space), 并将请求参数上的+编码为%2B.如果你希望将+当成空白符(space), 那么请直接使用一个空格 或者直接将其编码为%20.Custom Expansion@Param注解有一个可选的参数expander可以用来控制单个参数的展开行为(expansion), 该属性的值必须指向一个实现了Expander接口的类:public interface Expander { String expand(Object value);}对该方法的返回值的处理与上述规则相同, 如果返回值是null或者是一个空字符串, 那么该值会被忽略. 如果返回值不是使用pct编码(pct-encoded)的, 将会自动转换成pct编码. 可以从 Custom @Param Expansion 一节查看更多示例.Request Headers Expansion@Headers和HeaderMap模板对 Request Parameter Expansion 一节阐述的规则做以下修改, 并遵循之:未被解析的值将会被忽略, 但如果解析到一个空的值(empty header value), 那么对应的请求头会被移除.不会对请求头使用pct编码(pct-encoding).可以从Headers一节查看示例.关于@Param参数和参数名需要注意的点无论是在@RequestLine、@QueryMap、@BodyTemplate还是@Headers上的表达式, 只要表达式内的变量名字相同, 那么它们的值也必然相同. 如下面的例子, contentType的值会同时被解析到请求头(header)和路径(path)上:public interface ContentService { @RequestLine(“GET /api/documents/{contentType}”) @Headers(“Accept: {contentType}”) String getDocumentByType(@Param(“contentType”) String type);}当你在设计你的接口的一定要牢记这一点.Reuqest Body ExpansionBody模板对 Request Parameter Expansion 一节阐述的规则做以下修改, 并遵循之:未被解析的值将会被忽略.展开的值在被解析到请求体之前不会经过Encoder处理.必须指定Content-Type请求头, 可以从 Body Templates一节查看示例.Customization你可以在很多地方对Feign进行定制. 比如, 你可以使用Feign.builder()对自定义的组件构建API接口:interface Bank { @RequestLine(“POST /account/{id}”) Account getAccountInfo(@Param(“id”) String id);}public class BankService { public static void main(String[] args) { Bank bank = Feign.builder().decoder( new AccountDecoder()) .target(Bank.class, “https://api.examplebank.com”); }}Multiple InterfacesFeign客户以对使用Target<T>(默认是HardCodedTarget<T>)定义的对象生成多个API接口, 这样你可以在执行前动态发现服务或者对请求进行装饰.例如, 下面的代码可以实现为从身份服务中获取当前url和授权令牌(auth token), 然后设置到每个请求上:public class CloudService { public static void main(String[] args) { CloudDNS cloudDNS = Feign.builder() .target(new CloudIdentityTarget<CloudDNS>(user, apiKey)); } class CloudIdentityTarget extends Target<CloudDNS> { /* implementation of a Target / }}ExamplesFeign包含了GitHub和Wikipedia的客户端示例代码, 在实践中也可以参考这些项目, 尤其是example daemon.IntegrationsFeign在设计上就希望能够和其他开源项目很好的整合到一起, 我们也很乐于将你喜欢的模块添加进来.GsonGson包含了和JSON接口相关的编码(GsonEncoder)、解码器(GsonDecoder), 将它将它用到Feign.Builder的方式如下:public class Example { public static void main(String[] args) { GsonCodec codec = new GsonCodec(); GitHub github = Feign.builder() .encoder(new GsonEncoder()) .decoder(new GsonDecoder()) .target(GitHub.class, “https://api.github.com”); }}JacksonJackson包含了和JSON接口相关的编码(JacksonEncoder)、解码器(JacksonDecoder), 将它将它用到Feign.Builder的方式如下:public class Example { public static void main(String[] args) { GitHub github = Feign.builder() .encoder(new JacksonEncoder()) .decoder(new JacksonDecoder()) .target(GitHub.class, “https://api.github.com”); }}SaxSaxDecoder提供了可以与普通JVM和Android环境兼容的方式解析XML文本, 下面的例子展示了如何使用:public class Example { public static void main(String[] args) { Api api = Feign.builder() .decoder(SAXDecoder.builder() .registerContentHandler(UserIdHandler.class) .build()) .target(Api.class, “https://apihost”); }}JAXBJAXB包含了和XML接口相关的编码器(JAXBEncoder)、解码器(JAXBEncoder), 将它将它用到Feign.Builder的方式如下:public class Example { public static void main(String[] args) { Api api = Feign.builder() .encoder(new JAXBEncoder()) .decoder(new JAXBDecoder()) .target(Api.class, “https://apihost”); }}JAX-RSJAXRSContract使用JAX-RS规范提供的标准覆盖了对注解的处理, 目前实现的是1.1版的规范, 示例如下:interface GitHub { @GET @Path("/repos/{owner}/{repo}/contributors") List<Contributor> contributors(@PathParam(“owner”) String owner, @PathParam(“repo”) String repo);}public class Example { public static void main(String[] args) { GitHub github = Feign.builder() .contract(new JAXRSContract()) .target(GitHub.class, “https://api.github.com”); }}OkHttpOkHttpClient直接将Feign的http请求直接交由OkHttp处理, 后者实现了SPDY协议和提供了更好的网络控制能力.将OkHttp整合到Feign中需要你把OkHttp模块放到classpath下, 然后做如下配置:public class Example { public static void main(String[] args) { GitHub github = Feign.builder() .client(new OkHttpClient()) .target(GitHub.class, “https://api.github.com”); }}RibbonRibbonClient会覆盖Feign客户端的URL解析, 以实现由Ribbon提供的智能路由和弹性能力.将Ribbon与Feign整合需要你将url中的主机名(host)部分替换成Ribbon客户端名. 例如Ribbon客户端明为myAppProd:public class Example { public static void main(String[] args) { MyService api = Feign.builder() .client(RibbonClient.create()) .target(MyService.class, “https://myAppProd”); }}Java 11 Http2Http2Client直接将Feign的http请求交给Java11 New HTTP/2 Client处理, 后者实现了HTTP/2协议.要将New HTTP/2 Client与Feign整合使用, 你需要使用Java SDK 11, 并做如下配置:GitHub github = Feign.builder() .client(new Http2Client()) .target(GitHub.class, “https://api.github.com”);HystrixHystrixFeign实现了由Hystrix提供的断路器功能.要将Hystrix与Feign整合, 你需要将Hystrix模块放到classpath下, 并使用HystrixFeign:public class Example { public static void main(String[] args) { MyService api = HystrixFeign.builder().target(MyService.class, “https://myAppProd”); }}SOAPSOAP包含了XML接口相关的编码器(SOAPEncoder)、解码器(SOAPDecoder).该模块通过JAXB和SOAPMessage实现了对SOAP Body的编码和解码的支持, 通过将SOAPFault包装秤javax.xml.ws.soap.SOAPFaultException实现了对SOAPFault解码的功能, 因此, 对于SOAPFault的处理, 你只需要捕获SOAPFaultException.使用示例如下:public class Example { public static void main(String[] args) { Api api = Feign.builder() .encoder(new SOAPEncoder(jaxbFactory)) .decoder(new SOAPDecoder(jaxbFactory)) .errorDecoder(new SOAPErrorDecoder()) .target(MyApi.class, “http://api”); }}如果SOAP Faults的响应使用了表示错误的状态码(4xx, 5xx, …)的话, 那么你还需要添加一个SOAPErrorDecoder.SLF4JSLF4JModule实现了将Feign的日志重定向到SLF4J, 这允许你很容易的就能使用你想用的日志后端(Logback、Log4J等).要将SLF4J与Feign整合, 你需要将SLF4J模块和对应的日志后端模块放到classpath下, 并做如下配置:public class Example { public static void main(String[] args) { GitHub github = Feign.builder() .logger(new Slf4jLogger()) .target(GitHub.class, “https://api.github.com”); }}DecodersFeign.builder()允许你手动指定额外的配置, 如配置如何对响应进行解析.如果你接口定义的方法的返回值是除了Response、String、byte[]或void之外的类型, 那么你必须配置一个非默认的Decoder.下面的代码展示了如何配置使用feign-gson对JSON解码:public class Example { public static void main(String[] args) { GitHub github = Feign.builder() .decoder(new GsonDecoder()) .target(GitHub.class, “https://api.github.com”); }}如果你想在对响应进行解码之前先对其做处理的话, 你可以使用mapAndDecode方法, 下面的代码展示了对一个jsonp响应的处理, 在将响应交给JSON解码器之前, 需要先对jsonp做处理:public class Example { public static void main(String[] args) { JsonpApi jsonpApi = Feign.builder() .mapAndDecode((response, type) -> jsopUnwrap(response, type), new GsonDecoder()) .target(JsonpApi.class, “https://some-jsonp-api.com”); }}Encoders将一个请求体发送到服务器的最简单的办法是定义一个POST请求方法, 该方法的参数类型是String或byte[], 且参数上不带任何注解, 并且你可能还需要设置Content-Type请求头(如果没有的话):interface LoginClient { @RequestLine(“POST /”) @Headers(“Content-Type: application/json”) void login(String content);}public class Example { public static void main(String[] args) { client.login("{"user_name": "denominator", "password": "secret"}"); }}而通过配置Encoder, 你可以发送一个类型安全的请求体, 下面的例子展示了使用feign-gson扩展来实现编码:static class Credentials { final String user_name; final String password; Credentials(String user_name, String password) { this.user_name = user_name; this.password = password; }}interface LoginClient { @RequestLine(“POST /”) void login(Credentials creds);}public class Example { public static void main(String[] args) { LoginClient client = Feign.builder() .encoder(new GsonEncoder()) .target(LoginClient.class, “https://foo.com”); client.login(new Credentials(“denominator”, “secret”)); }}@Body templates使用@Body注解的模板会使用@Param注解的值来展开模板内部的表达式, 对于POST请求你可能还需要设置Content-Type请求头(如果没有的话):interface LoginClient { @RequestLine(“POST /”) @Headers(“Content-Type: application/xml”) @Body("<login "user_name"="{user_name}" "password"="{password}"/>") void xml(@Param(“user_name”) String user, @Param(“password”) String password); @RequestLine(“POST /”) @Headers(“Content-Type: application/json”) // json curly braces must be escaped! @Body("%7B"user_name": "{user_name}", "password": "{password}"%7D") void json(@Param(“user_name”) String user, @Param(“password”) String password);}public class Example { public static void main(String[] args) { client.xml(“denominator”, “secret”); // <login “user_name”=“denominator” “password”=“secret”/> client.json(“denominator”, “secret”); // {“user_name”: “denominator”, “password”: “secret”} }}HeadersFeign支持在api上为每个请求设置请求头, 也支持为每个客户端的请求设置请求头, 你可以根据实际场景进行选择.Set headers using apis对于那些明确需要设置某些请求头的接口的情况, 适用于将请求头的定义作为接口的一部分.静态配置的请求头可以通过在接口上使用@Headers注解设置:@Headers(“Accept: application/json”)interface BaseApi<V> { @Headers(“Content-Type: application/json”) @RequestLine(“PUT /api/{key}”) void put(@Param(“key”) String key, V value);}也可以在方法上的@Headers使用变量展开动态指定请求头的内容:public interface Api { @RequestLine(“POST /”) @Headers(“X-Ping: {token}”) void post(@Param(“token”) String token);}有时候, 对于同一个接口或客户端的请求头, 其键和值可能会随着不同的方法调用而发生变化, 且不可预知(例如: 自定义元数据请求头字段"x-amz-meta-“或"x-goog-meta-”), 此时可以在接口上声明一个Map参数, 并使用@HeaderMap注解将Map的内容设置为对应请求的请求头:public interface Api { @RequestLine(“POST /”) void post(@HeaderMap Map<String, Object> headerMap);}上述的几个方法都可以在接口上指定请求的请求头, 且不需要在构造时对Feign客户端做任何的定制.Setting headers per target当同一个接口的请求需要针对不同的请求对象(endpoints)配置不同的请求头, 或者需要对同一个接口的每个请求都定制其请求头时, 可以在Feign客户端上使用RequestInterceptor或Target来设置请求头.使用RequestInterceptor设置请求头的例子可以在Request Interceptor一节中查看示例.使用Target设置请求头的示例如下: static class DynamicAuthTokenTarget<T> implements Target<T> { public DynamicAuthTokenTarget(Class<T> clazz, UrlAndTokenProvider provider, ThreadLocal<String> requestIdProvider); @Override public Request apply(RequestTemplate input) { TokenIdAndPublicURL urlAndToken = provider.get(); if (input.url().indexOf(“http”) != 0) { input.insert(0, urlAndToken.publicURL); } input.header(“X-Auth-Token”, urlAndToken.tokenId); input.header(“X-Request-ID”, requestIdProvider.get()); return input.request(); } } public class Example { public static void main(String[] args) { Bank bank = Feign.builder() .target(new DynamicAuthTokenTarget(Bank.class, provider, requestIdProvider)); } }上述方法的最终效果取决于你对RequestInterceptor或Target内部的实现, 可以通过这种方法对每个Feign客户端的所有接口调用设置请求头. 这在一些场景下是非常有用的, 如对每个Feign客户端的所有请求设置认证令牌authentication token. 这些方法是在接口调用者所在的线程中执行的(译者注: 需要注意线程安全), 因此请求头的值可以是在调用时根据上下文动态地设置. 例如, 可以根据不同的调用线程, 从ThreadLocal里读取不同的数据设置请求头.Advanced usageBase Apis大多数情况下服务的接口都遵循相同的约定. Feign使用单继承的方式来实现, 比如下面的例子:interface BaseAPI { @RequestLine(“GET /health”) String health(); @RequestLine(“GET /all”) List<Entity> all();}你可以通过继承的方式来拥有BaseAPI的接口, 并实现其他特定的接口:interface CustomAPI extends BaseAPI { @RequestLine(“GET /custom”) String custom();}很多时候, 接口对资源的表示也是一致的, 因此, 也可以在基类的接口中使用泛型参数:@Headers(“Accept: application/json”)interface BaseApi<V> { @RequestLine(“GET /api/{key}”) V get(@Param(“key”) String key); @RequestLine(“GET /api”) List<V> list(); @Headers(“Content-Type: application/json”) @RequestLine(“PUT /api/{key}”) void put(@Param(“key”) String key, V value);}interface FooApi extends BaseApi<Foo> { }interface BarApi extends BaseApi<Bar> { }Logging你可以通过为Feign客户端设置Logger来记录其http日志, 最简单的实现如下:public class Example { public static void main(String[] args) { GitHub github = Feign.builder() .decoder(new GsonDecoder()) .logger(new Logger.JavaLogger().appendToFile(“logs/http.log”)) .logLevel(Logger.Level.FULL) .target(GitHub.class, “https://api.github.com”); }}Request Interceptors如果你需要跨Feign客户端对所有请求都做修改, 那么你可以配置RequestInterceptor来实现. 例如, 如果你是请求的一个代理, 那么你可能会需要设置X-Forwarded-For请求头:static class ForwardedForInterceptor implements RequestInterceptor { @Override public void apply(RequestTemplate template) { template.header(“X-Forwarded-For”, “origin.host.com”); }}public class Example { public static void main(String[] args) { Bank bank = Feign.builder() .decoder(accountDecoder) .requestInterceptor(new ForwardedForInterceptor()) .target(Bank.class, “https://api.examplebank.com”); }}另一个常见的使用拦截器的场景是授权, 比如使用内置的BasicAuthRequestInterceptor:public class Example { public static void main(String[] args) { Bank bank = Feign.builder() .decoder(accountDecoder) .requestInterceptor(new BasicAuthRequestInterceptor(username, password)) .target(Bank.class, “https://api.examplebank.com”); }}Custom @Param Expansion使用@Param注解的参数会用其toString()方法展开获得参数值, 也可以通过制定一个自定义的Param.Expander来控制. 如对日期的格式化:public interface Api { @RequestLine(“GET /?since={date}”) Result list(@Param(value = “date”, expander = DateToMillis.class) Date date);}Dynamic Query Parameters可以通过对Map类型的参数加上QueryMap注解, 将Map的内容构造成查询参数(query parameters):public interface Api { @RequestLine(“GET /find”) V find(@QueryMap Map<String, Object> queryMap);}同样的, 也可以通过使用QueryMapEncoder实现用POJO对象生成查询参数(query parameter):public interface Api { @RequestLine(“GET /find”) V find(@QueryMap CustomPojo customPojo);}当用这种方式时, 如果没有指定一个自定义的QueryMapEncoder, 那么查询参数的(query parameter)内容将根据对象的成员变量生成, 参数名对应变量名. 下面的例子中, 根据POJO对象生成的查询参数(query parameter)的内容是"/find?name={name}&number={number}", 生成的查询参数的顺序是不固定的, 按照惯例, 如果POJO对象的某个变量值为null, 那么该变量会被丢弃.public class CustomPojo { private final String name; private final int number; public CustomPojo (String name, int number) { this.name = name; this.number = number; }}设置自定义QueryMapEncoder的方式如下:public class Example { public static void main(String[] args) { MyApi myApi = Feign.builder() .queryMapEncoder(new MyCustomQueryMapEncoder()) .target(MyApi.class, “https://api.hostname.com”); }}当用@QueryMao注解时, 默认的编码器(encoder)会对对象的字段使用反射来将其展开成查询参数(query string). 如果希望通过对象的getter和setter方法来展开查询参数(query string), 请使用BeanQueryMapEncoder:public class Example { public static void main(String[] args) { MyApi myApi = Feign.builder() .queryMapEncoder(new BeanQueryMapEncoder()) .target(MyApi.class, “https://api.hostname.com”); }}Error Handling你可以通过在Feign实例构造时注册一个自定义的ErrorDecoder来实现对非正常响应的控制:public class Example { public static void main(String[] args) { MyApi myApi = Feign.builder() .errorDecoder(new MyErrorDecoder()) .target(MyApi.class, “https://api.hostname.com”); }}所有HTTP状态码不为2xx的响应都会触发ErrorDecoder的decode方法, 在这个方法内你可以对这些响应针对性地抛出异常, 或做其他额外的处理. 如果希望对请求进行重试, 那么可以抛出RetryableException, 该异常会触发Retryer.Retry默认情况下, Feign会对产生IOException的请求自动重试, 无论使用的是哪种HTTP方法, 都认为IOExcdeption是由短暂的网络问题产生的. 对ErrorDecoder内抛出的RetryableException也会进行请求重试. 你也可以通在Feign实例构造时设置自定义的Retryer来定制重试行为:public class Example { public static void main(String[] args) { MyApi myApi = Feign.builder() .retryer(new MyRetryer()) .target(MyApi.class, “https://api.hostname.com”); }}Retryer的实现需要决定一个请求是否应该进行重试, 可以通过continueOrPropagate(RetryableException e)方法的返回值(true或false)来实现. 每个Feign客户端执行时都会构造一个Retryer实例, 这样的话你可以维护每个请求的重新状态.如果最终重试也失败了, 那么会抛出RetryException, 如果希望抛出导致重试失败的异常, 可以在构造Feign客户端时指定exceptionPropagationPolicy()选项.Static and Default Methods使用Feign的接口可能是静态的或默认的方法(Java 8及以上支持), 这允许Feign客户端包含一些不适用底层接口定义的逻辑. 例如, 使用静态方法可以很轻易地指定通用客户端构造配置, 使用默认方法可以用于组合查询或定义默认参数:interface GitHub { @RequestLine(“GET /repos/{owner}/{repo}/contributors”) List<Contributor> contributors(@Param(“owner”) String owner, @Param(“repo”) String repo); @RequestLine(“GET /users/{username}/repos?sort={sort}”) List<Repo> repos(@Param(“username”) String owner, @Param(“sort”) String sort); default List<Repo> repos(String owner) { return repos(owner, “full_name”); } /* * Lists all contributors for all repos owned by a user. */ default List<Contributor> contributors(String user) { MergingContributorList contributors = new MergingContributorList(); for(Repo repo : this.repos(owner)) { contributors.addAll(this.contributors(user, repo.getName())); } return contributors.mergeResult(); } static GitHub connect() { return Feign.builder() .decoder(new GsonDecoder()) .target(GitHub.class, “https://api.github.com”); }} ...

February 26, 2019 · 7 min · jiezi

翻译:Hystrix - How To Use

转载请注明出处: 翻译:Hystrix - How To UseHello World!下面的代码展示了HystrixCommand版的Hello World:public class CommandHelloWorld extends HystrixCommand<String> { private final String name; public CommandHelloWorld(String name) { super(HystrixCommandGroupKey.Factory.asKey(“ExampleGroup”)); this.name = name; } @Override protected String run() { // a real example would do work like a network call here return “Hello " + name + “!”; }}查看源码HystrixObservableCommand的同等实现如下:public class CommandHelloWorld extends HystrixObservableCommand<String> { private final String name; public CommandHelloWorld(String name) { super(HystrixCommandGroupKey.Factory.asKey(“ExampleGroup”)); this.name = name; } @Override protected Observable<String> construct() { return Observable.create(new Observable.OnSubscribe<String>() { @Override public void call(Subscriber<? super String> observer) { try { if (!observer.isUnsubscribed()) { // a real example would do work like a network call here observer.onNext(“Hello”); observer.onNext(name + “!”); observer.onCompleted(); } } catch (Exception e) { observer.onError(e); } } } ).subscribeOn(Schedulers.io()); }}Synchronous Execution可以通过调用HystrixCommand.execute()方法实现同步执行, 示例如下:String s = new CommandHelloWorld(“World”).execute();测试如下: @Test public void testSynchronous() { assertEquals(“Hello World!”, new CommandHelloWorld(“World”).execute()); assertEquals(“Hello Bob!”, new CommandHelloWorld(“Bob”).execute()); }HystrixObservableCommand不提供同步执行方法, 但是如果确定其只会产生一个值, 那么也可以用如下方式实现:HystrixObservableCommand.observe().observe().toBlocking().toFuture().get()HystrixObservableCommand.toObservable().observe().toBlocking().toFuture().get()如果实际上产生了多个值, 上述的代码将会抛出java.lang.IllegalArgumentException: Sequence contains too many elements.Asynchronous Execution可以通过调用HystrixCommand.queue()方法实现异步执行, 示例如下:Future<String> fs = new CommandHelloWorld(“World”).queue();此时可以通过Future.get()方法获取command执行结果:String s = fs.get();测试代码如下: @Test public void testAsynchronous1() throws Exception { assertEquals(“Hello World!”, new CommandHelloWorld(“World”).queue().get()); assertEquals(“Hello Bob!”, new CommandHelloWorld(“Bob”).queue().get()); } @Test public void testAsynchronous2() throws Exception { Future<String> fWorld = new CommandHelloWorld(“World”).queue(); Future<String> fBob = new CommandHelloWorld(“Bob”).queue(); assertEquals(“Hello World!”, fWorld.get()); assertEquals(“Hello Bob!”, fBob.get()); }下面的两种实现是等价的:String s1 = new CommandHelloWorld(“World”).execute();String s2 = new CommandHelloWorld(“World”).queue().get();HystrixObservableCommand不提供queue方法, 但是如果确定其只会产生一个值, 那么也可以用如下方式实现:HystrixObservableCommand.observe().observe().toBlocking().toFuture()HystrixObservableCommand.toObservable().observe().toBlocking().toFuture()如果实际上产生了多个值, 上述的代码将会抛出java.lang.IllegalArgumentException: Sequence contains too many elements.Reactive Execution你也可以将HystrixCommand当做一个可观察对象(Observable)来观察(Observe)其产生的结果, 可以使用以下任意一个方法实现:observe(): 一旦调用该方法, 请求将立即开始执行, 其利用ReplaySubject特性可以保证不会丢失任何command产生的结果, 即使结果在你订阅之前产生的也不会丢失.toObservable(): 调用该方法后不会立即执行请求, 而是当有订阅者订阅时才会执行.Observable<String> ho = new CommandHelloWorld(“World”).observe();// or Observable<String> co = new CommandHelloWorld(“World”).toObservable();然后你可以通过订阅到这个Observable来取得command产生的结果:ho.subscribe(new Action1<String>() { @Override public void call(String s) { // value emitted here }});测试如下:@Testpublic void testObservable() throws Exception { Observable<String> fWorld = new CommandHelloWorld(“World”).observe(); Observable<String> fBob = new CommandHelloWorld(“Bob”).observe(); // blocking assertEquals(“Hello World!”, fWorld.toBlockingObservable().single()); assertEquals(“Hello Bob!”, fBob.toBlockingObservable().single()); // non-blocking // - this is a verbose anonymous inner-class approach and doesn’t do assertions fWorld.subscribe(new Observer<String>() { @Override public void onCompleted() { // nothing needed here } @Override public void onError(Throwable e) { e.printStackTrace(); } @Override public void onNext(String v) { System.out.println(“onNext: " + v); } }); // non-blocking // - also verbose anonymous inner-class // - ignore errors and onCompleted signal fBob.subscribe(new Action1<String>() { @Override public void call(String v) { System.out.println(“onNext: " + v); } });}使用Java 8的Lambda表达式可以使代码更简洁: fWorld.subscribe((v) -> { System.out.println(“onNext: " + v); }) // - or while also including error handling fWorld.subscribe((v) -> { System.out.println(“onNext: " + v); }, (exception) -> { exception.printStackTrace(); })关于Observable的信息可以在这里查阅Reactive Commands相比将HystrixCommand使用上述方法转换成一个Observable, 你也可以选择创建一个HystrixObservableCommand对象. HystrixObservableCommand包装的Observable允许产生多个结果(译者注: Subscriber.onNext可以调用多次), 而HystrixCommand即使转换成了Observable也只能产生一个结果.使用HystrixObservableCommnad时, 你需要重载construct方法来实现你的业务逻辑, 而不是重载run方法, contruct方法将会返回你需要包装的Observable.使用下面任意一个方法可以从HystrixObservableCommand中获取Observable对象:observe(): 一旦调用该方法, 请求将立即开始执行, 其利用ReplaySubject特性可以保证不会丢失任何command产生的结果, 即使结果在你订阅之前产生的也不会丢失.toObservable(): 调用该方法后不会立即执行请求, 而是当有订阅者订阅时才会执行.Fallback大多数情况下, 我们都希望command在执行失败时能够有一个候选方法来处理, 如: 返回一个默认值或执行其他失败处理逻辑, 除了以下几个情况:执行写操作的command: 当command的目标是执行写操作而不是读操作, 那么通常需要将写操作失败的错误交给调用者处理.批处理系统/离线计算: 如果command的目标是做一些离线计算、生成报表、填充缓存等, 那么同样应该将失败交给调用者处理.无论command是否实现了getFallback()方法, command执行失败时, Hystrix的状态和断路器(circuit-breaker)的状态/指标都会进行更新.HystrixCommand可以通过实现getFallback()方法来实现降级处理, run()方法异常、执行超时、线程池或信号量已满拒绝提供服务、断路器短路时, 都会调用getFallback():public class CommandHelloFailure extends HystrixCommand<String> { private final String name; public CommandHelloFailure(String name) { super(HystrixCommandGroupKey.Factory.asKey(“ExampleGroup”)); this.name = name; } @Override protected String run() { throw new RuntimeException(“this command always fails”); } @Override protected String getFallback() { return “Hello Failure " + name + “!”; }}查看源码这个命令的run()方法总是会执行失败, 但是调用者总是能收到getFallback()方法返回的值, 而不是收到一个异常: @Test public void testSynchronous() { assertEquals(“Hello Failure World!”, new CommandHelloFailure(“World”).execute()); assertEquals(“Hello Failure Bob!”, new CommandHelloFailure(“Bob”).execute()); }HystrixObservableCommand可以通过重载resumeWithFallback方法实现原Observable执行失败时返回回另一个Observable, 需要注意的是, 原Observable有可能在发出多个结果之后才出现错误, 因此在fallback实现的逻辑中不应该假设订阅者只会收到失败逻辑中发出的结果.Hystrix内部使用了RxJava的onErrorResumeNext操作符来实现Observable之间的无缝转移.Error Propagation除HystrixBadRequestException异常外, run方法中抛出的所有异常都会被认为是执行失败且会触发getFallback()方法和断路器的逻辑.你可以在HystrixBadRequestException中包装想要抛出的异常, 然后通过getCause()方法获取. HystrixBadRequestException使用在不应该被错误指标(failure metrics)统计和不应该触发getFallback()方法的场景, 例如报告参数不合法或者非系统异常等.对于HystrixObservableCommand, 不可恢复的错误都会在通过onError方法通知, 并通过获取用户实现的resumeWithFallback()方法返回的Observable来完成回退机制.执行异常类型Failure TypeException classException.causeFAILUREHystrixRuntimeExceptionunderlying exception(user-controlled)TIMEOUTHystrixRuntimeExceptionj.u.c.TimeoutExceptionSHORT_CIRCUITEDHystrixRuntimeExceptionj.l.RuntimeExceptionTHREAD_POOL_REJECTEDHystrixRuntimeExceptionj.u.c.RejectedExecutionExceptionSEMAPHORE_REJECTEDHystrixRuntimeExceptionj.l.RuntimeExceptionBAD_REQUESTHystrixBadRequestExceptionunderlying exception(user-controller)Command Name默认的command name是从类名中派生的:getClass().getSimpleName()可以通过HystrixCommand或HystrixObservableCommand的构造器来指定command name: public CommandHelloWorld(String name) { super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(“ExampleGroup”)) .andCommandKey(HystrixCommandKey.Factory.asKey(“HelloWorld”))); this.name = name; }可以通过如下方式来重用Setter: private static final Setter cachedSetter = Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(“ExampleGroup”)) .andCommandKey(HystrixCommandKey.Factory.asKey(“HelloWorld”)); public CommandHelloWorld(String name) { super(cachedSetter); this.name = name; }HystrixCommandKey是一个接口, 因此可以将其实现为一个枚举或者常规的类, 但是它已经内置了一个Factory类来构建帮助构建内部实例, 使用方式如下:HystrixCommandKey.Factory.asKey(“Hello World”);Command GroupHystrix使用command group来为分组, 分组信息主要用于报告、警报、仪表盘上显示, 或者是标识团队/库的拥有者.默认情况下, 除非已经用这个名字定义了一个信号量, 否则 Hystrix将使用这个名称来定义command的线程池.HystrixCommandGroupKey是一个接口, 因此可以将其实现为一个枚举或者常规的类, 但是它已经内置了一个Factory类来构建帮助构建内部实例, 使用方式如下:HystrixCommandGroupKey.Factory.asKey(“Example Group”)Command Thread-poolthread-pool key主要用于在监控、指标发布、缓存等类似场景中标识一个HystrixThreadPool, 一个HystrixCommand于其构造函数中传入的HystrixThreadPoolKey指定的HystrixThreadPool相关联, 如果未指定的话, 则使用HystrixCommandGroupKey来获取/创建HystrixThreadPool.可以通过HystrixCommand或HystrixObservableCommand的构造器来指定其值: public CommandHelloWorld(String name) { super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(“ExampleGroup”)) .andCommandKey(HystrixCommandKey.Factory.asKey(“HelloWorld”)) .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey(“HelloWorldPool”))); this.name = name; }HystrixCommandThreadPoolKey是一个接口, 因此可以将其实现为一个枚举或者常规的类, 但是它已经内置了一个Factory类来构建帮助构建内部实例, 使用方式如下:HystrixThreadPoolKey.Factory.asKey(“Hello World Pool”)使用HystrixThreadPoolKey而不是使用不同的HystrixCommandGroupKey的原因是: 可能会有多条command在逻辑功能上属于同一个组(group), 但是其中的某些command需要和其他command隔离开, 例如:两条用于访问视频元数据的command两条command的group name都是VideoMetadatacommand A与资源#1互斥command B与资源#2互斥如果command A由于延迟等原因导致其所在的线程池资源耗尽, 不应该影响command B对#2的执行, 因为他们访问的是不同的后端资源.因此, 从逻辑上来说, 我们希望这两条command应该被分到同一个分组, 但是我们同样系统将这两条命令的执行隔离开来, 因此我们使用HystrixThreadPoolKey将其分配到不同的线程池.Request Cache可以通过实现HystrixCommand或HystrixObservableCommand的getCacheKey()方法开启用对请求的缓存功能:public class CommandUsingRequestCache extends HystrixCommand<Boolean> { private final int value; protected CommandUsingRequestCache(int value) { super(HystrixCommandGroupKey.Factory.asKey(“ExampleGroup”)); this.value = value; } @Override protected Boolean run() { return value == 0 || value % 2 == 0; } @Override protected String getCacheKey() { return String.valueOf(value); }}由于该功能依赖于请求的上下文信息, 因此我们必须初始化一个HystrixRequestContext, 使用方式如下: @Test public void testWithoutCacheHits() { HystrixRequestContext context = HystrixRequestContext.initializeContext(); try { assertTrue(new CommandUsingRequestCache(2).execute()); assertFalse(new CommandUsingRequestCache(1).execute()); assertTrue(new CommandUsingRequestCache(0).execute()); assertTrue(new CommandUsingRequestCache(58672).execute()); } finally { context.shutdown(); } }通常情况下, 上下文信息(HystrixRequestContext)应该在持有用户请求的ServletFilter或者其他拥有生命周期管理功能的类来初始化和关闭.下面的例子展示了command如何从缓存中获取数据, 以及如何查询一个数据是否是从缓存中获取到的: @Test public void testWithCacheHits() { HystrixRequestContext context = HystrixRequestContext.initializeContext(); try { CommandUsingRequestCache command2a = new CommandUsingRequestCache(2); CommandUsingRequestCache command2b = new CommandUsingRequestCache(2); assertTrue(command2a.execute()); // this is the first time we’ve executed this command with // the value of “2” so it should not be from cache assertFalse(command2a.isResponseFromCache()); assertTrue(command2b.execute()); // this is the second time we’ve executed this command with // the same value so it should return from cache assertTrue(command2b.isResponseFromCache()); } finally { context.shutdown(); } // start a new request context context = HystrixRequestContext.initializeContext(); try { CommandUsingRequestCache command3b = new CommandUsingRequestCache(2); assertTrue(command3b.execute()); // this is a new request context so this // should not come from cache assertFalse(command3b.isResponseFromCache()); } finally { context.shutdown(); } }Request Collapsing请求合并可以用于将多条请求绑定到一起, 由同一个HystrixCommand实例执行.collapser可以通过batch size和batch创建以来的耗时来自动将请求合并执行.Hystrix支持两个请求合并方式: 请求级的合并和全局级的合并. 默认是请求范围的合并, 可以在构造collapser时指定值.请求级(request-scoped)的collapser只会合并每一个HystrixRequestContext中的请求, 而全局级(globally-scoped)的collapser则可以跨HystrixRequestContext合并请求. 因此, 如果你下游的依赖者无法再一个command中处理多个HystrixRequestContext的话, 那么你应该使用请求级的合并.在Netflix, 我们只会使用请求级的合并, 因为我们当前所有的系统都是基于一个command对应一个HystrixRequestContext的设想下构建的. 因此, 当一个command使用不同的参数在一个请求中并发执行时, 合并是有效的.下面的代码展示了如何实现请求级的HystrixCollapser:public class CommandCollapserGetValueForKey extends HystrixCollapser<List<String>, String, Integer> { private final Integer key; public CommandCollapserGetValueForKey(Integer key) { this.key = key; } @Override public Integer getRequestArgument() { return key; } @Override protected HystrixCommand<List<String>> createCommand(final Collection<CollapsedRequest<String, Integer>> requests) { return new BatchCommand(requests); } @Override protected void mapResponseToRequests(List<String> batchResponse, Collection<CollapsedRequest<String, Integer>> requests) { int count = 0; for (CollapsedRequest<String, Integer> request : requests) { request.setResponse(batchResponse.get(count++)); } } private static final class BatchCommand extends HystrixCommand<List<String>> { private final Collection<CollapsedRequest<String, Integer>> requests; private BatchCommand(Collection<CollapsedRequest<String, Integer>> requests) { super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(“ExampleGroup”)) .andCommandKey(HystrixCommandKey.Factory.asKey(“GetValueForKey”))); this.requests = requests; } @Override protected List<String> run() { ArrayList<String> response = new ArrayList<String>(); for (CollapsedRequest<String, Integer> request : requests) { // artificial response for each argument received in the batch response.add(“ValueForKey: " + request.getArgument()); } return response; } }}下面的代码展示了如果使用collapser自动合并4个CommandCollapserGetValueForKey到一个HystrixCommand中执行:@Testpublic void testCollapser() throws Exception { HystrixRequestContext context = HystrixRequestContext.initializeContext(); try { Future<String> f1 = new CommandCollapserGetValueForKey(1).queue(); Future<String> f2 = new CommandCollapserGetValueForKey(2).queue(); Future<String> f3 = new CommandCollapserGetValueForKey(3).queue(); Future<String> f4 = new CommandCollapserGetValueForKey(4).queue(); assertEquals(“ValueForKey: 1”, f1.get()); assertEquals(“ValueForKey: 2”, f2.get()); assertEquals(“ValueForKey: 3”, f3.get()); assertEquals(“ValueForKey: 4”, f4.get()); // assert that the batch command ‘GetValueForKey’ was in fact // executed and that it executed only once assertEquals(1, HystrixRequestLog.getCurrentRequest().getExecutedCommands().size()); HystrixCommand<?> command = HystrixRequestLog.getCurrentRequest().getExecutedCommands().toArray(new HystrixCommand<?>[1])[0]; // assert the command is the one we’re expecting assertEquals(“GetValueForKey”, command.getCommandKey().name()); // confirm that it was a COLLAPSED command execution assertTrue(command.getExecutionEvents().contains(HystrixEventType.COLLAPSED)); // and that it was successful assertTrue(command.getExecutionEvents().contains(HystrixEventType.SUCCESS)); } finally { context.shutdown(); }}Request Context Setup使用请求级的特性时(如: 请求缓存、请求合并、请求日志)你必须管理HystrixRequestContext的生命周期(或者实现HystrixConcurrencyStategy).这意味着你必须在请求之前执行如下代码:HystrixRequestContext context = HystrixRequestContext.initializeContext();并在请求结束后执行如下代码:context.shutdown();在标准的Java web应用中, 你可以使用Setvlet Filter实现的如下的过滤器来管理:public class HystrixRequestContextServletFilter implements Filter { public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HystrixRequestContext context = HystrixRequestContext.initializeContext(); try { chain.doFilter(request, response); } finally { context.shutdown(); } }}可以在web.xml中加入如下代码实现对所有的请求都使用该过滤器: <filter> <display-name>HystrixRequestContextServletFilter</display-name> <filter-name>HystrixRequestContextServletFilter</filter-name> <filter-class>com.netflix.hystrix.contrib.requestservlet.HystrixRequestContextServletFilter</filter-class> </filter> <filter-mapping> <filter-name>HystrixRequestContextServletFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>Common Patterns以下是HystrixCommand和HystrixObservableCommand的一般用法和使用模式.Fail Fast最基本的使用是执行一条只做一件事情且没有实现回退方法的command, 这样的command在发生任何错误时都会抛出异常:public class CommandThatFailsFast extends HystrixCommand<String> { private final boolean throwException; public CommandThatFailsFast(boolean throwException) { super(HystrixCommandGroupKey.Factory.asKey(“ExampleGroup”)); this.throwException = throwException; } @Override protected String run() { if (throwException) { throw new RuntimeException(“failure from CommandThatFailsFast”); } else { return “success”; } }下面的代码演示了上述行为:@Testpublic void testSuccess() { assertEquals(“success”, new CommandThatFailsFast(false).execute());}@Testpublic void testFailure() { try { new CommandThatFailsFast(true).execute(); fail(“we should have thrown an exception”); } catch (HystrixRuntimeException e) { assertEquals(“failure from CommandThatFailsFast”, e.getCause().getMessage()); e.printStackTrace(); }}HystrixObservableCommand需要重载resumeWithFallback()方法来实现同样的行为: @Override protected Observable<String> resumeWithFallback() { if (throwException) { return Observable.error(new Throwable(“failure from CommandThatFailsFast”)); } else { return Observable.just(“success”); } }Fail Silent静默失败等同于返回一个空的响应或者移除功能. 可以是返回null、空Map、空List, 或者其他类似的响应.可以通过实现HystrixCommand.getFallback()方法实现该功能:public class CommandThatFailsSilently extends HystrixCommand<String> { private final boolean throwException; public CommandThatFailsSilently(boolean throwException) { super(HystrixCommandGroupKey.Factory.asKey(“ExampleGroup”)); this.throwException = throwException; } @Override protected String run() { if (throwException) { throw new RuntimeException(“failure from CommandThatFailsFast”); } else { return “success”; } } @Override protected String getFallback() { return null; }}@Testpublic void testSuccess() { assertEquals(“success”, new CommandThatFailsSilently(false).execute());}@Testpublic void testFailure() { try { assertEquals(null, new CommandThatFailsSilently(true).execute()); } catch (HystrixRuntimeException e) { fail(“we should not get an exception as we fail silently with a fallback”); }}或者返回一个空List的实现如下: @Override protected List<String> getFallback() { return Collections.emptyList(); }HystrixObservableCommand可以通过重载resumeWithFallback()方法实现同样的行为: @Override protected Observable<String> resumeWithFallback() { return Observable.empty(); }Fallback: StaticFallback可以返回代码里设定的默认值, 这种方式可以通过默认行为来有效避免于静默失败带来影响.例如, 如果一个应返回true/false的用户认证的command执行失败了, 那么其默认行为可以如下: @Override protected Boolean getFallback() { return true; }对于HystrixObservableCommand可以通过重载resumeWithFallback()方法实现同样的行为: @Override protected Observable<Boolean> resumeWithFallback() { return Observable.just( true ); }Fallback: Stubbed当command返回的是一个包含多个字段的复合对象, 且该对象的一部分字段值可以通过其他请求状态获得, 另一部分状态可以通过设置默认值获得时, 你通常需要使用存根(stubbed)模式.你可能可以从存根值(stubbed values)中得到适当的值的情况如下:cookies请求参数和请求头当前失败请求的前一个服务请求的响应在fallback代码块内可以静态地获取请求范围内的存根(stubbed)值, 但是通常我们更推荐在构建command实例时注入这些值, 就像下面实例的代码中的countryCodeFromGeoLookup一样:public class CommandWithStubbedFallback extends HystrixCommand<UserAccount> { private final int customerId; private final String countryCodeFromGeoLookup; /** * @param customerId * The customerID to retrieve UserAccount for * @param countryCodeFromGeoLookup * The default country code from the HTTP request geo code lookup used for fallback. / protected CommandWithStubbedFallback(int customerId, String countryCodeFromGeoLookup) { super(HystrixCommandGroupKey.Factory.asKey(“ExampleGroup”)); this.customerId = customerId; this.countryCodeFromGeoLookup = countryCodeFromGeoLookup; } @Override protected UserAccount run() { // fetch UserAccount from remote service // return UserAccountClient.getAccount(customerId); throw new RuntimeException(“forcing failure for example”); } @Override protected UserAccount getFallback() { /* * Return stubbed fallback with some static defaults, placeholders, * and an injected value ‘countryCodeFromGeoLookup’ that we’ll use * instead of what we would have retrieved from the remote service. / return new UserAccount(customerId, “Unknown Name”, countryCodeFromGeoLookup, true, true, false); } public static class UserAccount { private final int customerId; private final String name; private final String countryCode; private final boolean isFeatureXPermitted; private final boolean isFeatureYPermitted; private final boolean isFeatureZPermitted; UserAccount(int customerId, String name, String countryCode, boolean isFeatureXPermitted, boolean isFeatureYPermitted, boolean isFeatureZPermitted) { this.customerId = customerId; this.name = name; this.countryCode = countryCode; this.isFeatureXPermitted = isFeatureXPermitted; this.isFeatureYPermitted = isFeatureYPermitted; this.isFeatureZPermitted = isFeatureZPermitted; } }}下面的代码演示了上述行为: @Test public void test() { CommandWithStubbedFallback command = new CommandWithStubbedFallback(1234, “ca”); UserAccount account = command.execute(); assertTrue(command.isFailedExecution()); assertTrue(command.isResponseFromFallback()); assertEquals(1234, account.customerId); assertEquals(“ca”, account.countryCode); assertEquals(true, account.isFeatureXPermitted); assertEquals(true, account.isFeatureYPermitted); assertEquals(false, account.isFeatureZPermitted); }对于HystrixObservableCommand可以通过重载resumeWithFallback()方法实现同样的行为:@Overrideprotected Observable<Boolean> resumeWithFallback() { return Observable.just( new UserAccount(customerId, “Unknown Name”, countryCodeFromGeoLookup, true, true, false) );}如果你想要从Observable中发出多个值, 那么当失败发生时, 原本的Observable可能已经发出的一部分值, 此时你或许更希望能够只从fallback逻辑中发出另一部分未被发出的值, 下面的例子就展示了如何实现这一个目的: 它通过追踪原Observable发出的最后一个值来实现fallback逻辑中的Observable应该从什么地方继续发出存根值(stubbed value) :@Overrideprotected Observable<Integer> construct() { return Observable.just(1, 2, 3) .concatWith(Observable.<Integer> error(new RuntimeException(“forced error”))) .doOnNext(new Action1<Integer>() { @Override public void call(Integer t1) { lastSeen = t1; } }) .subscribeOn(Schedulers.computation());}@Overrideprotected Observable<Integer> resumeWithFallback() { if (lastSeen < 4) { return Observable.range(lastSeen + 1, 4 - lastSeen); } else { return Observable.empty(); }}Fallback: Cache via Network有时后端的服务异常也会引起command执行失败, 此时我们也可以从缓存中(如: memcached)取得相关的数据.由于在fallback的逻辑代码中访问网络可能会再次失败, 因此必须构建新的HystrixCommand或HystrixObservableCommand来执行:很重要的一点是执行fallback逻辑的command需要在一个不同的线程池中执行, 否则如果原command的延迟变高且其所在线程池已经满了的话, 执行fallback逻辑的command将无法在同一个线程池中执行.下面的代码展示了CommandWithFallbackViaNetwork如何在getFallback()方法中执行FallbackViaNetwork.注意, FallbackViaNetwork同样也具有回退机制, 这里通过返回null来实现fail silent.FallbackViaNetwork默认会从HystrixCommandGroupKey中继承线程池的配置RemoteServiceX, 因此需要在其构造器中注入HystrixThreadPoolKey.Factory.asKey(“RemoteServiceXFallback”)来使其在不同的线程池中执行.这样, CommandWithFallbackViaNetwork会在名为RemoteServiceX的线程池中执行, 而FallbackViaNetwork会在名为RemoteServiceXFallback的线程池中执行.public class CommandWithFallbackViaNetwork extends HystrixCommand<String> { private final int id; protected CommandWithFallbackViaNetwork(int id) { super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(“RemoteServiceX”)) .andCommandKey(HystrixCommandKey.Factory.asKey(“GetValueCommand”))); this.id = id; } @Override protected String run() { // RemoteServiceXClient.getValue(id); throw new RuntimeException(“force failure for example”); } @Override protected String getFallback() { return new FallbackViaNetwork(id).execute(); } private static class FallbackViaNetwork extends HystrixCommand<String> { private final int id; public FallbackViaNetwork(int id) { super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(“RemoteServiceX”)) .andCommandKey(HystrixCommandKey.Factory.asKey(“GetValueFallbackCommand”)) // use a different threadpool for the fallback command // so saturating the RemoteServiceX pool won’t prevent // fallbacks from executing .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey(“RemoteServiceXFallback”))); this.id = id; } @Override protected String run() { MemCacheClient.getValue(id); } @Override protected String getFallback() { // the fallback also failed // so this fallback-of-a-fallback will // fail silently and return null return null; } }}Primary + Secondary with Fallback有些系统可能具有是以双系统模式搭建的 — 主从模式或主备模式.有时从系统或备用系统会被认为是失败状态的一种, 仅在执行fallback逻辑是才使用它;这种场景和Cache via Network一节中描述的场景是一样的.然而, 如果切换到从系统是一个很正常时, 例如发布新代码时(这是有状态的系统发布代码的一种方式), 此时每当切换到从系统使用时, 主系统都是处于不可用状态,断路器将会打开且发出警报.这并不是我们期望发生的事, 这种狼来了式的警报可能会导致真正发生问题的时候我们却把它当成正常的误报而忽略了.因此, 我们可以通过在其前面放置一个门面HystrixCommand(见下文), 将主/从系统的切换视为正常的、健康的状态.主从HystrixCommand都是需要访问网络且实现了特定的业务逻辑, 因此其实现上应该是线程隔离的. 它们可能具有显著的性能差距(通常从系统是一个静态缓存), 因此将两个command隔离的另一个好处是可以针对性地调优.你不需要将这两个command都公开发布, 只需要将它们隐藏在另一个由信号量隔离的HystrixCommand中(称之为门面HystrixCommand), 在这个command中去实现主系统还是从系统的调用选择. 只有当主从系统都失败了, 才会去执行这个门面command的fallback逻辑.门面HystrixCommand可以使用信号量隔离的, 因为其业务逻辑仅仅是调用另外两个线程隔离的HystrixCommand, 它不涉及任何的网络访问、重试等容易出错的事, 因此没必要将这部分代码放到其他线程去执行.public class CommandFacadeWithPrimarySecondary extends HystrixCommand<String> { private final static DynamicBooleanProperty usePrimary = DynamicPropertyFactory.getInstance().getBooleanProperty(“primarySecondary.usePrimary”, true); private final int id; public CommandFacadeWithPrimarySecondary(int id) { super(Setter .withGroupKey(HystrixCommandGroupKey.Factory.asKey(“SystemX”)) .andCommandKey(HystrixCommandKey.Factory.asKey(“PrimarySecondaryCommand”)) .andCommandPropertiesDefaults( // we want to default to semaphore-isolation since this wraps // 2 others commands that are already thread isolated HystrixCommandProperties.Setter() .withExecutionIsolationStrategy(ExecutionIsolationStrategy.SEMAPHORE))); this.id = id; } @Override protected String run() { if (usePrimary.get()) { return new PrimaryCommand(id).execute(); } else { return new SecondaryCommand(id).execute(); } } @Override protected String getFallback() { return “static-fallback-” + id; } @Override protected String getCacheKey() { return String.valueOf(id); } private static class PrimaryCommand extends HystrixCommand<String> { private final int id; private PrimaryCommand(int id) { super(Setter .withGroupKey(HystrixCommandGroupKey.Factory.asKey(“SystemX”)) .andCommandKey(HystrixCommandKey.Factory.asKey(“PrimaryCommand”)) .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey(“PrimaryCommand”)) .andCommandPropertiesDefaults( // we default to a 600ms timeout for primary HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(600))); this.id = id; } @Override protected String run() { // perform expensive ‘primary’ service call return “responseFromPrimary-” + id; } } private static class SecondaryCommand extends HystrixCommand<String> { private final int id; private SecondaryCommand(int id) { super(Setter .withGroupKey(HystrixCommandGroupKey.Factory.asKey(“SystemX”)) .andCommandKey(HystrixCommandKey.Factory.asKey(“SecondaryCommand”)) .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey(“SecondaryCommand”)) .andCommandPropertiesDefaults( // we default to a 100ms timeout for secondary HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(100))); this.id = id; } @Override protected String run() { // perform fast ‘secondary’ service call return “responseFromSecondary-” + id; } } public static class UnitTest { @Test public void testPrimary() { HystrixRequestContext context = HystrixRequestContext.initializeContext(); try { ConfigurationManager.getConfigInstance().setProperty(“primarySecondary.usePrimary”, true); assertEquals(“responseFromPrimary-20”, new CommandFacadeWithPrimarySecondary(20).execute()); } finally { context.shutdown(); ConfigurationManager.getConfigInstance().clear(); } } @Test public void testSecondary() { HystrixRequestContext context = HystrixRequestContext.initializeContext(); try { ConfigurationManager.getConfigInstance().setProperty(“primarySecondary.usePrimary”, false); assertEquals(“responseFromSecondary-20”, new CommandFacadeWithPrimarySecondary(20).execute()); } finally { context.shutdown(); ConfigurationManager.getConfigInstance().clear(); } } }}Client Doesn’t Perform Network Access当你使用HystrixCommand实现的业务逻辑不涉及到网络访问、对延迟敏感且无法接受多线程带来的开销时, 你需要设置executionIsolationStrategy)属性的值为ExecutionIsolationStrategy.SEMAPHORE, 此时Hystrix会使用信号量隔离代替线程隔离.下面的代码展示了如何为command设置该属性(也可以在运行时动态改变这个属性的值):public class CommandUsingSemaphoreIsolation extends HystrixCommand<String> { private final int id; public CommandUsingSemaphoreIsolation(int id) { super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(“ExampleGroup”)) // since we’re doing an in-memory cache lookup we choose SEMAPHORE isolation .andCommandPropertiesDefaults(HystrixCommandProperties.Setter() .withExecutionIsolationStrategy(ExecutionIsolationStrategy.SEMAPHORE))); this.id = id; } @Override protected String run() { // a real implementation would retrieve data from in memory data structure return “ValueFromHashMap_” + id; }}Get-Set-Get with Request Cache InvalidationGet-Set-Get是指: Get请求的结果被缓存下来后, 另一个command对同一个资源发出了Set请求, 此时由Get请求缓存的结果应该失效, 避免随后的Get请求获取到过时的缓存结果, 此时可以通过调用HystrixRequestCache.clear())方法来使缓存失效.public class CommandUsingRequestCacheInvalidation { / represents a remote data store / private static volatile String prefixStoredOnRemoteDataStore = “ValueBeforeSet_”; public static class GetterCommand extends HystrixCommand<String> { private static final HystrixCommandKey GETTER_KEY = HystrixCommandKey.Factory.asKey(“GetterCommand”); private final int id; public GetterCommand(int id) { super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(“GetSetGet”)) .andCommandKey(GETTER_KEY)); this.id = id; } @Override protected String run() { return prefixStoredOnRemoteDataStore + id; } @Override protected String getCacheKey() { return String.valueOf(id); } /* * Allow the cache to be flushed for this object. * * @param id * argument that would normally be passed to the command */ public static void flushCache(int id) { HystrixRequestCache.getInstance(GETTER_KEY, HystrixConcurrencyStrategyDefault.getInstance()).clear(String.valueOf(id)); } } public static class SetterCommand extends HystrixCommand<Void> { private final int id; private final String prefix; public SetterCommand(int id, String prefix) { super(HystrixCommandGroupKey.Factory.asKey(“GetSetGet”)); this.id = id; this.prefix = prefix; } @Override protected Void run() { // persist the value against the datastore prefixStoredOnRemoteDataStore = prefix; // flush the cache GetterCommand.flushCache(id); // no return value return null; } }} @Test public void getGetSetGet() { HystrixRequestContext context = HystrixRequestContext.initializeContext(); try { assertEquals(“ValueBeforeSet_1”, new GetterCommand(1).execute()); GetterCommand commandAgainstCache = new GetterCommand(1); assertEquals(“ValueBeforeSet_1”, commandAgainstCache.execute()); // confirm it executed against cache the second time assertTrue(commandAgainstCache.isResponseFromCache()); // set the new value new SetterCommand(1, “ValueAfterSet_”).execute(); // fetch it again GetterCommand commandAfterSet = new GetterCommand(1); // the getter should return with the new prefix, not the value from cache assertFalse(commandAfterSet.isResponseFromCache()); assertEquals(“ValueAfterSet_1”, commandAfterSet.execute()); } finally { context.shutdown(); } } }Migrating a Library to Hystrix如果你要迁移一个已有的客户端库到Hystrix, 你应该将所有的服务方法(service methods)替换成HystrixCommand.服务方法(service methods)转而调用HystrixCommand且不在包含任何额外的业务逻辑.因此, 在迁移之前, 一个服务库可能是这样的:迁移完成之后, 服务库的用户要能直接访问到HystrixCommand, 或者通过服务门面(service facade)的代理间接访问到HystrixCommand. ...

February 25, 2019 · 11 min · jiezi

百度翻译API实战

参考官方文档:定制化翻译API技术文档所需传输参数百度翻译的API所需的除了需要翻译的内容和指定语言外,比较麻烦的是需要制作3个授权认证相关的参数。正式调用APIAPI地址:https://fanyi-api.baidu.com/api/trans/vip/translate千万要看清楚这个地址中的vip,而不是官方文档里的private。真是个大坑呢。提交方式:GET 或 POST参数设置(Params或者Body都可以):在Postman中选择Bulk-edit,加入以下内容:q:{{query}}from:ento:zhappid:{{appid}}salt:{{salt}}sign:{{sign}}选择环境变量,将这几个环境变量加进去:并且根据自己的内容填进去。除了填写这些,我们还需要一些自动的脚本来处理数据,因为百度的认证比较麻烦。在Postman里面选择Pre-script,把脚本加进去:// URL request example: // “https://fanyi-api.baidu.com/api/trans/vip/translate?q=apple&from=en&to=zh&appid=2015063000000001&salt=1435660288&sign=f89f9594663708c1605f3d736d01d2d4"var query = pm.environment.get(“query”);var appid = pm.environment.get(“appid”);var salt = (new Date).getTime();var key = pm.environment.get(‘secret_key’);var sign_string = appid + query + salt + key;var sign = CryptoJS.MD5(sign_string).toString();// set encoded query textpm.environment.set(“query”, encodeURI(query));// Set a random number to “salt"pm.environment.set(“salt”, salt);// set hashed “sign” value for authenticationpm.environment.set(“sign”, sign);然后就可以点击Send发送了。以下是百度翻译返回的内容:

January 26, 2019 · 1 min · jiezi

翻译:github如何记录contributions

github contributions一般而言,任何一个人的github主页都有一个这样的方格图;每一个小格代表了one day,不同的颜色深度,代表了不同的contributions次数,那么这个次数是怎么计算的呢?本文的翻译就是上图底部的蓝色链接中的Learn how we count contributions.译文正文为什么我的contributions没有在个人profile中体现出来?个人profile中的contributions绘图是用于记录个人对github所做的contributions记录,贡献按照协调世界时(UTC)而不是您当地的时区加上时间戳。只有在满足特定标准的情况下才会当做contributions计算;在某些情况下,我们可能需要重建您的contributions绘图才能显示你的contributions。没有被记录的contributionsIssues和pull requests如果Issues和pull requests只有是在独立仓库中操作的,才会显示在您的contributions绘图上,而对于fork的仓库无法被记录的。Commits如果Commits符合以下所有条件,则会在您的contributions绘图上显示:用于Commits的电子邮件地址与您的GitHub帐户相关联Commits是在独立的仓库中进行的,而不是forkCommits在一下条件下完成在仓库的默认分支(通常是master)在gh-pages分支中(对于具有Project Pages站点的仓库)此外,必须至少满足下列条件之一:你是该仓库的协作者,或者是这个仓库所属组织的一员;你已经fork了该工程你对这个仓库提过Issues或者pull requests你star加星过这个仓库,通常的一些没有被记录的原因要显示在您的contributions绘图上,共同提交的提交必须符合与一个作者的提交相同的标准。当合并pull requests并且Commit时,只有合并pull requests的用户和打开pull requests的用户才会收到贡献积分。拉取请求的任何其他贡献者都不会获得贡献积分。当重新提交Commit时,Commit的原始作者和重新提交的人,无论是在命令行还是在GitHub上,都会收到贡献信用。Commit是在不到24小时前完成在完成符合要求的Commit后,您可能需要等待最多24小时才能看到贡献出现在您的贡献图表上。您尚未将本地Git提交电子邮件添加到您的个人资料中必须使用已添加到您的GitHub个人资料中的电子邮件地址进行提交,以便显示在您的贡献图表上。您可以通过将.patch添加到提交URL的末尾来检查用于提交的电子邮件地址,例如:https://github.com/octocat/octocat.github.io/commit/67c0afc1da354d8571f51b6f0af8f2794117fd10.patchFrom 67c0afc1da354d8571f51b6f0af8f2794117fd10 Mon Sep 17 00:00:00 2001From: The Octocat <octocat@nowhere.com>Date: Sun, 27 Apr 2014 15:36:39 +0530Subject: [PATCH] updated index for better welcome message可以配合:查看email:git config –global user.email 设置email:git config –global user.email XXXX@gmail.com未在默认master或gh-pages分支中进行提交只有在默认分支(通常为master)或gh-pages分支(对于具有Project Pages站点的存储库)中进行提交时才会计算提交。提交共同作者无权访问存储库如果在共同作者无权访问的存储库中进行了提交,则该提交将不计入共同作者的贡献。提交是在一个fork分支中进行的用fork做的提交不会计入你的贡献。要使它们计数,您必须执行以下操作之一:打开pull requests以将更改合并到父存储库中。要分离fork并将其转换为GitHub上的独立存储库,请联系GitHub支持或GitHub Premium支持。如果fork具有自己的分支,请让支持知道分支是否应随存储库移动到新网络中或保留在当前网络中。Commit是在合并和压缩的pull requests中完成的合并和压缩的pull requests中的Commit将不计入您的贡献。只有合并pull requests的用户和打开pull requests的用户才会收到贡献积分。拉取请求的任何其他贡献者都不会获得贡献积分。

January 26, 2019 · 1 min · jiezi

【翻译】两行代码解决 RavenDB 性能问题

原文标题:Changing Fundamental Behavior With Two Lines of Code(用两行代码改变基础行为)作者:Oren Eini, CEO RavenDB一直以来我们对 RavenDB 的启动时间并没有太多关注 —— 5 秒或者 15 秒都不是什么问题。但是如果超过 15 秒,甚至长达 3 分钟的话,那我们就必须关注了。我们的一个用户提供了一个有意思的用例。他们的系统运行在 Azure 上,并充分利用了多设备存储,也就是将数据库临时文件(journals)放在高性能存储服务上,而数据本身放在更大(且相对较慢)的普通存储上。这么做是因为他们的数据量很大,比如某条索引的大小就超过了 256GB。在他们系统当中,RavenDB 的启动慢到无法接受。我们经过排查发现根本原因在于启动过程中的数据恢复阶段,此时数据库会重新执行临时文件中的最近事务,以保证数据完整性。通常来讲这步花不了多少时间,因为默认情况下,即使数据库负载较大时,临时文件大小也只有 256MB 左右。但我们这位客户的使用场景比较特殊,我们观察到临时文件中一个事务的数据量能达到几个 GB 的大小,再加上临时文件的内容是经过压缩的,所以当数据恢复到主存储当中时,实际的数据量会达到 10GB 以上。虽然那些已经执行过的事务不需要重复执行,但我们必须要先扫描临时文件,来找到哪部分是已经执行过的。在这个基础上,考虑到数据库的个数,以及每个数据库的索引个数,RavenDB 启动时的整体消耗就变得十分可观了。显然这不是我们所要的。如果我们遇到系统崩溃,那么目前还没有什么好的办法来避免重新执行这些事务;但问题是就算没有遇到崩溃,就算是正常关闭,重新启动的过程还是一样的缓慢。这个问题本质上是因为 RavenDB 没有在临时文件中标记哪个位置是已经同步过的,所以在启动过程中我们必须扫描整个临时文件,来找到需要重新执行的部分。当然我们可以在临时文件中补上这个标记,但是我们不能直接去更改客户现有系统的数据文件格式,这种解决方案不但感觉别扭,而且可能带来兼容性问题。我们也考虑修改数据库的行为,使得在对某个大数据量的事务同步成功后,更加主动的切换到另一个临时文件。今天我在查看相关代码时,发现了一个有问题的地方。每当我们处理一个大事务(事务大小超过临时文件最大大小)时,为了有足够的空间,我们会在磁盘上扩充临时文件的大小,而我们分配空间的计算方式很有意思:如图所示,如果当前的临时文件大小小于需要的最小空间,我们会要增加其空间;同时为了避免过于频繁的扩充文件操作,我们还会考虑给下一个事务预留足够的空间。现在我们假设当前临时文件大小为 256MB(也是系统预设的最大值),而事务大小为 1.56GB。这种情况下,临时文件将会扩充到 2GB 大小,而其中只有 1.56GB 用到了。本来剩余的空间我们是可以继续利用的,除非下一个事务很大,比如有 800MB,我们就会要重建一个新的 1GB 大小的文件。这个时候问题来了。对于这个 2GB 大小的临时文件,假设我们已经成功将其内容同步到了主存储,那么剩下还有 440MB 的空间可用,我们就会保留这个临时文件用它来存储下一个事务。如果在这个点上数据库进行了重启,那么在启动阶段就必须扫描整个 2GB 的临时文件来确保没有丢失数据。要修复这个问题也超简单:我们要做的就是当扩充后的大小大于临时文件最大大小时,取临时文件最大大小和实际需要的大小之间的最大值作为最终的实际大小。这样扩充后的临时文件就刚好只能容纳一个大事务。当这个事务成功同步后,因为没有额外的空间再容纳另一个事务,于是 Voron 便会马上清理这个文件。这样临时文件中就不存在残留的大事务数据。这个改动既简洁又有效,非常棒,我非常喜欢。

January 16, 2019 · 1 min · jiezi

即将取代RNN结构的Transformer

Transformer之前上图是经典的双向RNN模型,我们知道该模型是通过递归的方式运行,虽然适合对序列数据建模,但是缺点也很明显“它无法并行执行”也就无法利用GPU强大的并行能力(这里插句题外话,正因为GPU强大的并行能力,所以batch_size等于1和等于200运算时间基本差不多),再加上各种门控机制,运行速度很慢。一般而言,编码器输出编码向量C作为解码器输入,但是由于编码向量C中所有的编码器输入值贡献相同,导致序列数据越长信息丢失越多。CNN网络相比RNN网络,它虽然可以并行执行,但是无法一次捕获全局信息,通过上图可得我们需要多次遍历,多个卷积层叠加增大感受野。谷歌的做法是Attention is All You Need !Transformer如图所示是Transformer的整体结构,我们将详细介绍每一部分,先从左边的编码器开始。A: 这一步,我想大家已经非常熟悉了,将词汇表转为embedding维度的向量(onehot和embedding区别)。B: 仅仅使用attention有一个致命短板,它对序列数据的顺序免疫,即:无法捕获序列的顺序。比如对翻译任务,我们知道顺序非常重要,单词顺序变动甚至会产生完全不同的意思。因此增加Position Embedding给每个位置编号,每个编号对应一个向量,这样每个词向量都会有一个位置向量,以此来定位。如图所示,Position Embedding计算公式,将id为p的位置映射为一个dpos维的位置向量,这个向量的第i个元素的数值就是PEi(p),位置编码算法当然不止一种,但是不同算法必须要解决的的问题就是能够处理未知长度的序列。假设位置向量有4维,实际位置向量可能如下所示:结合位置向量和词向量我们有两种方式,一种是将两者拼接成一个新向量,另一种是使两者维度相同然后相加得到新向量。C:残差连接,随后是D: layer-normalization。随着网络层数的加深,会带来梯度消失,梯度爆炸以及过拟合问题。针对此问题,我们一般采用权重正则化,批标准化,更换激活函数等等措施,但是当网络层数进一步增加,会出现网络退化问题,即:训练集精度开始下降。使用残差可以解决此问题,目前使用残差的网络可以达到几百层。E:Multi-head注意力机制上图是attention计算过程,我们分解步骤,依次来看。生成“q”,“k”,“v”向量,由输入embedding向量与图示右侧对应权重矩阵相乘。需要注意的是,此处的三个权重矩阵为所有输入共享。如果每个词独享一个权重矩阵,个人认为并不会提升性能,有可能还会降低。计算attention score,计算方式如图所示:使用softmax归一化数值,softmax上面的相除操作主要是调解内积不要太大。将softmax归一化后的值与“v”向量矩阵相乘,将所有加权向量加和,产生该位置的self-attention的输出结果。multi-headed attention:就是有多组上面那样的attention,最后将结果拼接起来,其中,每组attention权重不共享。计算公式如下:整体计算过程如下图所示:F:全连接网络,两个线性函数,一个非线性函数(Relu):解码器:A:解码器attention计算的内部向量和编码器的输出向量,计算源句和目标句之间的关系,在Transformer之前,attention机制就应用在这里。B:线性层是一个全连接层,神经元数量和词表长度相等,然后添加softmax层,将概率最高值对应的词作为输出。总结Transformer现在大有取代RNN之势,但依然存在一些缺点。首先,Transformer虽然使用到了位置向量,但是对序列位置要求很高的项目做的并不好。Transformer可以一步到位获取全局信息,但如果你的项目只是需要局部信息呢?虽然也可以做到,但是增加了多余计算量。本文内容部分参考The Illustrated Transformer

January 5, 2019 · 1 min · jiezi

webpack-chain项目中文翻译

webpack-chain注意:这是对原项目readme文件的翻译,为啥翻译这个呢,因为Vue CLI3脚手架生成的项目使用这种方式配置webpack,但是脚手架中对这块的介绍不多,所以把这部分翻译出来,以供团队和大家参考。应用一个链式 API 来生成和简化 2-4 版本的webpack的配置的修改。此文档对应于webpack-chain的v5版本,对于以前的版本,请参阅:v4 docsv3 docsv2 docsv1 docs注意: 虽然 webpack-chain 被广泛应用在Neutrino中,然而本软件包完全独立,可供任何项目使用。介绍webpack 的核心配置的创建和修改基于一个有潜在难于处理的 JavaScript 对象。虽然这对于配置单个项目来说还是 OK 的,但当你尝试跨项目共享这些对象并使其进行后续的修改就会变的混乱不堪,因为您需要深入了解底层对象的结构以进行这些更改。webpack-chain 尝试通过提供可链式或顺流式的 API 创建和修改webpack 配置。API的 Key 部分可以由用户指定的名称引用,这有助于 跨项目修改配置方式 的标准化。通过以下示例可以更容易地解释这一点。安装webpack-chain 需要 Node.js v6.9及更高版本. webpack-chain 也只创建并被设计于使用webpack的2,3,4版本的配置对象。你可以使用Yarn或者npm来安装此软件包(俩个包管理工具选一个就行):Yarn方式yarn add –dev webpack-chainnpm方式npm install –save-dev webpack-chain入门当你安装了 webpack-chain, 你就可以开始创建一个webpack的配置。 对于本指南,我们的示例基本配置 webpack.config.js 将位于我们项目的根目录。// 导入 webpack-chain 模块,该模块导出了一个用于创建一个webpack配置API的单一构造函数。const Config = require(‘webpack-chain’);// 对该单一构造函数创建一个新的配置实例const config = new Config();// 用链式API改变配置// 每个API的调用都会跟踪对存储配置的更改。config // 修改 entry 配置 .entry(‘index’) .add(‘src/index.js’) .end() // 修改 output 配置 .output .path(‘dist’) .filename(’[name].bundle.js’);// 创建一个具名规则,以后用来修改规则config.module .rule(’lint’) .test(/.js$/) .pre() .include .add(‘src’) .end() // 还可以创建具名use (loaders) .use(’eslint’) .loader(’eslint-loader’) .options({ rules: { semi: ‘off’ } });config.module .rule(‘compile’) .test(/.js$/) .include .add(‘src’) .add(’test’) .end() .use(‘babel’) .loader(‘babel-loader’) .options({ presets: [ [’@babel/preset-env’, { modules: false }] ] });// 也可以创建一个具名的插件!config .plugin(‘clean’) .use(CleanPlugin, [[‘dist’], { root: ‘/dir’ }]);// 导出这个修改完成的要被webpack使用的配置对象module.exports = config.toConfig();共享配置也很简单。仅仅导出配置 和 在传递给webpack之前调用 .toConfig() 方法将配置导出给webpack使用。// webpack.core.jsconst Config = require(‘webpack-chain’);const config = new Config();// 跨目标共享配置// Make configuration shared across targets// …module.exports = config;// webpack.dev.jsconst config = require(’./webpack.core’);// Dev-specific configuration// 开发具体配置// …module.exports = config.toConfig();// webpack.prod.jsconst config = require(’./webpack.core’);// Production-specific configuration// 生产具体配置// …module.exports = config.toConfig();ChainedMapwebpack-chain 中的核心API接口之一是 ChainedMap. 一个 ChainedMap的操作类似于JavaScript Map, 为链式和生成配置提供了一些便利。 如果一个属性被标记一个 ChainedMap, 则它将具有如下的API和方法:除非另有说明,否则这些方法将返回 ChainedMap , 允许链式调用这些方法。// 从 Map 移除所有 配置.clear()// 通过键值从 Map 移除单个配置.// key: *delete(key)// 获取 Map 中相应键的值// key: *// returns: valueget(key)// 获取 Map 中相应键的值// 如果键在Map中不存在,则ChainedMap中该键的值会被配置为fn的返回值.// key: *// fn: Function () -> value// returns: valuegetOrCompute(key, fn)// 配置Map中 已存在的键的值// key: *// value: *set(key, value)// Map中是否存在一个配置值的特定键,返回 真或假// key: *// returns: Booleanhas(key)// 返回 Map中已存储的所有值的数组// returns: Arrayvalues()// 返回Map中全部配置的一个对象, 其中 键是这个对象属性,值是相应键的值,// 如果Map是空,返回 undefined// 使用 .before() 或 .after() 的ChainedMap, 则将按照属性名进行排序。// returns: Object, undefined if emptyentries()// 提供一个对象,这个对象的属性和值将 映射进 Map。// 你也可以提供一个数组作为第二个参数以便忽略合并的属性名称。// obj: Object// omit: Optional Arraymerge(obj, omit)// 对当前配置上下文执行函数。// handler: Function -> ChainedMap // 一个把ChainedMap实例作为单个参数的函数batch(handler)// 条件执行一个函数去继续配置// condition: Boolean// whenTruthy: Function -> ChainedMap // 当条件为真,调用把ChainedMap实例作为单一参数传入的函数// whenFalsy: Optional Function -> ChainedMap // 当条件为假,调用把ChainedMap实例作为单一参数传入的函数when(condition, whenTruthy, whenFalsy)ChainedSetwebpack-chain 中的核心API接口另一个是 ChainedSet. 一个 ChainedSet的操作类似于JavaScript Map, 为链式和生成配置提供了一些便利。 如果一个属性被标记一个 ChainedSet, 则它将具有如下的API和方法:除非另有说明,否则这些方法将返回 ChainedSet , 允许链式调用这些方法。// 添加/追加 给Set末尾位置一个值.// value: *add(value)// 添加 给Set开始位置一个值.// value: prepend(value)// 移除Set中全部值.clear()// 移除Set中一个指定的值.// value: delete(value)// 检测Set中是否存在一个值.// value: // returns: Booleanhas(value)// 返回Set中值的数组.// returns: Arrayvalues()// 连接给定的数组到 Set 尾部。// arr: Arraymerge(arr)// 对当前配置上下文执行函数。// handler: Function -> ChainedSet // 一个把 ChainedSet 实例作为单个参数的函数batch(handler)// 条件执行一个函数去继续配置// condition: Boolean// whenTruthy: Function -> ChainedSet // 当条件为真,调用把 ChainedSet 实例作为单一参数传入的函数// whenFalsy: Optional Function -> ChainedSet // 当条件为假,调用把 ChainedSet 实例作为单一参数传入的函数when(condition, whenTruthy, whenFalsy)速记方法存在许多简写方法,用于 使用与简写方法名称相同的键在 ChainedMap 设置一个值例如, devServer.hot 是一个速记方法, 因此它可以用作:// 在 ChainedMap 上设置一个值的 速记方法devServer.hot(true);// 上述方法等效于:devServer.set(‘hot’, true);一个速记方法是可链式的,因此调用它将返回 原实例,允许你继续链式使用配置创建一个新的配置对象const Config = require(‘webpack-chain’);const config = new Config();移动到API的更深层将改变你正在修改的内容的上下文。 你可以通过 config在此引用顶级配置或者通过调用 .end() 方法向上移动一级 使你移回更高的 上下文环境。如果你熟悉jQuery, 这里与其 .end() 工作原理类似。除非另有说明,否则全部的API调用都将在当前上下文中返回API实例。 这样,你可以根据需要连续 链式API调用. 有关对所有速记和低级房费有效的特定值的详细信息,请参阅 webpack文档层次结构 中的相应名词。Config : ChainedMap配置速记方法config .amd(amd) .bail(bail) .cache(cache) .devtool(devtool) .context(context) .externals(externals) .loader(loader) .mode(mode) .parallelism(parallelism) .profile(profile) .recordsPath(recordsPath) .recordsInputPath(recordsInputPath) .recordsOutputPath(recordsOutputPath) .stats(stats) .target(target) .watch(watch) .watchOptions(watchOptions)配置 entryPoints// 回到 config.entryPoints : ChainedMapconfig.entry(name) : ChainedSetconfig .entry(name) .add(value) .add(value)config .entry(name) .clear()// 用低级别 config.entryPoints:config.entryPoints .get(name) .add(value) .add(value)config.entryPoints .get(name) .clear()配置 output: 速记 方法config.output : ChainedMapconfig.output .auxiliaryComment(auxiliaryComment) .chunkFilename(chunkFilename) .chunkLoadTimeout(chunkLoadTimeout) .crossOriginLoading(crossOriginLoading) .devtoolFallbackModuleFilenameTemplate(devtoolFallbackModuleFilenameTemplate) .devtoolLineToLine(devtoolLineToLine) .devtoolModuleFilenameTemplate(devtoolModuleFilenameTemplate) .filename(filename) .hashFunction(hashFunction) .hashDigest(hashDigest) .hashDigestLength(hashDigestLength) .hashSalt(hashSalt) .hotUpdateChunkFilename(hotUpdateChunkFilename) .hotUpdateFunction(hotUpdateFunction) .hotUpdateMainFilename(hotUpdateMainFilename) .jsonpFunction(jsonpFunction) .library(library) .libraryExport(libraryExport) .libraryTarget(libraryTarget) .path(path) .pathinfo(pathinfo) .publicPath(publicPath) .sourceMapFilename(sourceMapFilename) .sourcePrefix(sourcePrefix) .strictModuleExceptionHandling(strictModuleExceptionHandling) .umdNamedDefine(umdNamedDefine)配置 resolve(解析): 速记方法config.resolve : ChainedMapconfig.resolve .cachePredicate(cachePredicate) .cacheWithContext(cacheWithContext) .enforceExtension(enforceExtension) .enforceModuleExtension(enforceModuleExtension) .unsafeCache(unsafeCache) .symlinks(symlinks)配置 resolve 别名config.resolve.alias : ChainedMapconfig.resolve.alias .set(key, value) .set(key, value) .delete(key) .clear()配置 resolve modulesconfig.resolve.modules : ChainedSetconfig.resolve.modules .add(value) .prepend(value) .clear()配置 resolve aliasFieldsconfig.resolve.aliasFields : ChainedSetconfig.resolve.aliasFields .add(value) .prepend(value) .clear()配置 resolve descriptionFieldsconfig.resolve.descriptionFields : ChainedSetconfig.resolve.descriptionFields .add(value) .prepend(value) .clear()配置 resolve extensionsconfig.resolve.extensions : ChainedSetconfig.resolve.extensions .add(value) .prepend(value) .clear()配置 resolve mainFieldsconfig.resolve.mainFields : ChainedSetconfig.resolve.mainFields .add(value) .prepend(value) .clear()配置 resolve mainFilesconfig.resolve.mainFiles : ChainedSetconfig.resolve.mainFiles .add(value) .prepend(value) .clear()配置 resolveLoader当前API config.resolveLoader 相同于 配置 config.resolve 用下面的配置:配置 resolveLoader moduleExtensionsconfig.resolveLoader.moduleExtensions : ChainedSetconfig.resolveLoader.moduleExtensions .add(value) .prepend(value) .clear()配置 resolveLoader packageMainsconfig.resolveLoader.packageMains : ChainedSetconfig.resolveLoader.packageMains .add(value) .prepend(value) .clear()配置 performance(性能): 速记方法config.performance : ChainedMapconfig.performance .hints(hints) .maxEntrypointSize(maxEntrypointSize) .maxAssetSize(maxAssetSize) .assetFilter(assetFilter)配置 optimizations(优化): 速记方法config.optimization : ChainedMapconfig.optimization .concatenateModules(concatenateModules) .flagIncludedChunks(flagIncludedChunks) .mergeDuplicateChunks(mergeDuplicateChunks) .minimize(minimize) .namedChunks(namedChunks) .namedModules(namedModules) .nodeEnv(nodeEnv) .noEmitOnErrors(noEmitOnErrors) .occurrenceOrder(occurrenceOrder) .portableRecords(portableRecords) .providedExports(providedExports) .removeAvailableModules(removeAvailableModules) .removeEmptyChunks(removeEmptyChunks) .runtimeChunk(runtimeChunk) .sideEffects(sideEffects) .splitChunks(splitChunks) .usedExports(usedExports)配置 optimization minimizers(最小优化器)// 回到 config.optimization.minimizersconfig.optimization .minimizer(name) : ChainedMap配置 optimization minimizers: 添加注意: 不要用 new 去创建最小优化器插件,因为已经为你做好了。config.optimization .minimizer(name) .use(WebpackPlugin, args)// 例如config.optimization .minimizer(‘css’) .use(OptimizeCSSAssetsPlugin, [{ cssProcessorOptions: { safe: true } }])// Minimizer 插件也可以由它们的路径指定,从而允许在不使用插件或webpack配置的情况下跳过昂贵的 require s。config.optimization .minimizer(‘css’) .use(require.resolve(‘optimize-css-assets-webpack-plugin’), [{ cssProcessorOptions: { safe: true } }])配置 optimization minimizers: 修改参数config.optimization .minimizer(name) .tap(args => newArgs)// 例如config .minimizer(‘css’) .tap(args => […args, { cssProcessorOptions: { safe: false } }])配置 optimization minimizers: 修改实例config.optimization .minimizer(name) .init((Plugin, args) => new Plugin(…args));配置 optimization minimizers: 移除config.optimization.minimizers.delete(name)配置插件// 回到 config.pluginsconfig.plugin(name) : ChainedMap配置插件: 添加注意: 不要用 new 去创建插件,因为已经为你做好了。config .plugin(name) .use(WebpackPlugin, args)// 例如config .plugin(‘hot’) .use(webpack.HotModuleReplacementPlugin);// 插件也可以由它们的路径指定,从而允许在不使用插件或webpack配置的情况下跳过昂贵的 require s。config .plugin(’env’) .use(require.resolve(‘webpack/lib/EnvironmentPlugin’), [{ ‘VAR’: false }]);配置插件: 修改参数config .plugin(name) .tap(args => newArgs)// 例如config .plugin(’env’) .tap(args => […args, ‘SECRET_KEY’]);配置插件: 修改实例config .plugin(name) .init((Plugin, args) => new Plugin(…args));配置插件: 移除config.plugins.delete(name)配置插件: 在之前调用指定当前插件上下文应该在另一个指定插件之前执行,你不能在同一个插件上同时使用 .before() 和 .after()。config .plugin(name) .before(otherName)// 例如config .plugin(‘html-template’) .use(HtmlWebpackTemplate) .end() .plugin(‘script-ext’) .use(ScriptExtWebpackPlugin) .before(‘html-template’);Config plugins: 在之后调用指定当前插件上下文应该在另一个指定插件之后执行,你不能在同一个插件上同时使用 .before() 和 .after()。config .plugin(name) .after(otherName)// 例如config .plugin(‘html-template’) .after(‘script-ext’) .use(HtmlWebpackTemplate) .end() .plugin(‘script-ext’) .use(ScriptExtWebpackPlugin);配置 resolve 插件// 回到 config.resolve.pluginsconfig.resolve.plugin(name) : ChainedMap配置 resolve 插件: 添加注意: 不要用 new 去创建插件,因为已经为你做好了。config.resolve .plugin(name) .use(WebpackPlugin, args)配置 resolve 插件: 修改参数config.resolve .plugin(name) .tap(args => newArgs)配置 resolve 插件: 修改实例config.resolve .plugin(name) .init((Plugin, args) => new Plugin(…args))配置 resolve 插件: 移除config.resolve.plugins.delete(name)配置 resolve 插件: 在之前调用指定当前插件上下文应该在另一个指定插件之前执行,你不能在同一个插件上同时使用 .before() 和 .after()。config.resolve .plugin(name) .before(otherName)// 例如config.resolve .plugin(‘beta’) .use(BetaWebpackPlugin) .end() .plugin(‘alpha’) .use(AlphaWebpackPlugin) .before(‘beta’);配置 resolve 插件: 在之后调用指定当前插件上下文应该在另一个指定插件之后执行,你不能在同一个插件上同时使用 .before() 和 .after()。config.resolve .plugin(name) .after(otherName)// 例如config.resolve .plugin(‘beta’) .after(‘alpha’) .use(BetaWebpackTemplate) .end() .plugin(‘alpha’) .use(AlphaWebpackPlugin);配置 nodeconfig.node : ChainedMapconfig.node .set(’__dirname’, ‘mock’) .set(’__filename’, ‘mock’);配置 devServerconfig.devServer : ChainedMap配置 devServer allowedHostsconfig.devServer.allowedHosts : ChainedSetconfig.devServer.allowedHosts .add(value) .prepend(value) .clear()配置 devServer: 速记方法config.devServer .bonjour(bonjour) .clientLogLevel(clientLogLevel) .color(color) .compress(compress) .contentBase(contentBase) .disableHostCheck(disableHostCheck) .filename(filename) .headers(headers) .historyApiFallback(historyApiFallback) .host(host) .hot(hot) .hotOnly(hotOnly) .https(https) .inline(inline) .info(info) .lazy(lazy) .noInfo(noInfo) .open(open) .openPage(openPage) .overlay(overlay) .pfx(pfx) .pfxPassphrase(pfxPassphrase) .port(port) .progress(progress) .proxy(proxy) .public(public) .publicPath(publicPath) .quiet(quiet) .setup(setup) .socket(socket) .staticOptions(staticOptions) .stats(stats) .stdin(stdin) .useLocalIp(useLocalIp) .watchContentBase(watchContentBase) .watchOptions(watchOptions)配置 moduleconfig.module : ChainedMap配置 module: 速记方法config.module : ChainedMapconfig.module .noParse(noParse)配置 module rules: 速记方法config.module.rules : ChainedMapconfig.module .rule(name) .test(test) .pre() .post() .enforce(preOrPost)配置 module rules uses (loaders): 创建config.module.rules{}.uses : ChainedMapconfig.module .rule(name) .use(name) .loader(loader) .options(options)// Exampleconfig.module .rule(‘compile’) .use(‘babel’) .loader(‘babel-loader’) .options({ presets: [’@babel/preset-env’] });配置 module rules uses (loaders): 修改选项config.module .rule(name) .use(name) .tap(options => newOptions)// 例如config.module .rule(‘compile’) .use(‘babel’) .tap(options => merge(options, { plugins: [’@babel/plugin-proposal-class-properties’] }));配置 module rules oneOfs (条件 rules)config.module.rules{}.oneOfs : ChainedMap<Rule>config.module .rule(name) .oneOf(name)// 例如config.module .rule(‘css’) .oneOf(‘inline’) .resourceQuery(/inline/) .use(‘url’) .loader(‘url-loader’) .end() .end() .oneOf(’external’) .resourceQuery(/external/) .use(‘file’) .loader(‘file-loader’)合并配置webpack-chain 支持将对象合并到配置实例,改实例类似于 webpack-chain 模式 布局的布局。 请注意,这不是 webpack 配置对象,但您可以再将webpack配置对象提供给webpack-chain 以匹配器布局之前对其进行转换。config.merge({ devtool: ‘source-map’ });config.get(‘devtool’) // “source-map"config.merge({ [key]: value, amd, bail, cache, context, devtool, externals, loader, mode, parallelism, profile, recordsPath, recordsInputPath, recordsOutputPath, stats, target, watch, watchOptions, entry: { [name]: […values] }, plugin: { [name]: { plugin: WebpackPlugin, args: […args], before, after } }, devServer: { [key]: value, clientLogLevel, compress, contentBase, filename, headers, historyApiFallback, host, hot, hotOnly, https, inline, lazy, noInfo, overlay, port, proxy, quiet, setup, stats, watchContentBase }, node: { [key]: value }, optimizations: { concatenateModules, flagIncludedChunks, mergeDuplicateChunks, minimize, minimizer, namedChunks, namedModules, nodeEnv, noEmitOnErrors, occurrenceOrder, portableRecords, providedExports, removeAvailableModules, removeEmptyChunks, runtimeChunk, sideEffects, splitChunks, usedExports, }, performance: { [key]: value, hints, maxEntrypointSize, maxAssetSize, assetFilter }, resolve: { [key]: value, alias: { [key]: value }, aliasFields: […values], descriptionFields: […values], extensions: […values], mainFields: […values], mainFiles: […values], modules: […values], plugin: { [name]: { plugin: WebpackPlugin, args: […args], before, after } } }, resolveLoader: { [key]: value, alias: { [key]: value }, aliasFields: […values], descriptionFields: […values], extensions: […values], mainFields: […values], mainFiles: […values], modules: […values], moduleExtensions: […values], packageMains: […values], plugin: { [name]: { plugin: WebpackPlugin, args: […args], before, after } } }, module: { [key]: value, rule: { [name]: { [key]: value, enforce, issuer, parser, resource, resourceQuery, test, include: […paths], exclude: […paths], oneOf: { [name]: Rule }, use: { [name]: { loader: LoaderString, options: LoaderOptions, before, after } } } } }})条件配置当使用的情况下工作ChainedMap和ChainedSet,则可以使用执行条件的配置when。您必须指定一个表达式 when(),以评估其真实性或虚假性。如果表达式是真实的,则将使用当前链接实例的实例调用第一个函数参数。您可以选择提供在条件为假时调用的第二个函数,该函数也是当前链接的实例。// 示例:仅在生产期间添加minify插件config .when(process.env.NODE_ENV === ‘production’, config => { config .plugin(‘minify’) .use(BabiliWebpackPlugin); });// 例:只有在生产过程中添加缩小插件,否则设置devtool到源映射config .when(process.env.NODE_ENV === ‘production’, config => config.plugin(‘minify’).use(BabiliWebpackPlugin), config => config.devtool(‘source-map’) );检查生成的配置您可以使用检查生成的webpack配置config.toString()。这将生成配置的字符串化版本,其中包含命名规则,用法和插件的注释提示:config .module .rule(‘compile’) .test(/.js$/) .use(‘babel’) .loader(‘babel-loader’);config.toString();{ module: { rules: [ / config.module.rule(‘compile’) / { test: /.js$/, use: [ / config.module.rule(‘compile’).use(‘babel’) / { loader: ‘babel-loader’ } ] } ] }}默认情况下,如果生成的字符串包含需要的函数和插件,则不能直接用作真正的webpack配置。为了生成可用的配置,您可以通过__expression在其上设置特殊属性来自定义函数和插件的字符串化方式:class MyPlugin {}MyPlugin.__expression = require('my-plugin');function myFunction () {}myFunction.__expression = require('my-function');config .plugin(’example’) .use(MyPlugin, [{ fn: myFunction }]);config.toString();/{ plugins: [ new (require(‘my-plugin’))({ fn: require(‘my-function’) }) ]}/通过其路径指定的插件将require()自动生成其语句:config .plugin(’env’) .use(require.resolve(‘webpack/lib/ProvidePlugin’), [{ jQuery: ‘jquery’ }])config.toString();{ plugins: [ new (require(’/foo/bar/src/node_modules/webpack/lib/EnvironmentPlugin.js’))( { jQuery: ‘jquery’ } ) ]}您还可以调用toString静态方法Config,以便在字符串化之前修改配置对象。Config.toString({ …config.toConfig(), module: { defaultRules: [ { use: [ { loader: ‘banner-loader’, options: { prefix: ‘banner-prefix.txt’ }, }, ], }, ], },}){ plugins: [ / config.plugin(‘foo’) */ new TestPlugin() ], module: { defaultRules: [ { use: [ { loader: ‘banner-loader’, options: { prefix: ‘banner-prefix.txt’ } } ] } ] }} ...

December 28, 2018 · 6 min · jiezi

注意力机制实现机器翻译

介绍Attention模型形象的比喻就是“图像对焦”。上图是Encoder-Decoder模型,Decoder中每个单词生成过程如下:其中C是“语义编码C”,f是Decoder的非线性变换函数。由此,我们可以看出生成目标句子的每个单词都使用同一个语义编码C,即:源句子中的每个单词的影响力都是一样的,这如同图像没有对焦的情况,现实项目中也存在明显的不合理。比如一个机器翻译模型,输入是“Tom chase Jerry”,模型输出:“汤姆”,“追逐”,“杰瑞”。在翻译“杰瑞”的时候显然“Jerry”的贡献值最大,如果每个单词的贡献值相同明显不合理。这个问题在输入句子长度较短时问题不大,但是当输入句子较长时会丢失很多细节信息(个人觉得此处类似平均池化和最大值池化)。正因为如此,我们引入了Attention思想。Soft Attention模型使用Attention模型翻译“杰瑞”的时候,我们可以得到输入句子中的每个单词对输出当前单词的贡献值大小如:(Tom,0.3)(Chase,0.2) (Jerry,0.5)。这意味着生成每个单词yi时不再使用同一个语义编码C,而是根据yi使用不同的Ci。在引入Attention模型后yi的计算过程改变如下所示:每个Ci对应源句子中每个单词的注意力分配概率,示例如下:f2是Encoder对每个单词的变换函数,g函数代表整个源句子的中间语义表示的变换函数,一般形式是加权求和:aji代表注意力分配系数,hj代表源句子中某个单词的语义编码,Lx代表源句子中单词数量。g函数的计算过程如下图所示:Attention模型概率计算如果所示,当我们要生成yi单词,此时我们用i-1时刻的隐藏节点输出值Hi-1去和源句子中的每个单词对应RNN隐藏节点状态hj依次进行对比,即:通过函数F(hj,Hi-1)来获得yi对源句子中每个单词对应的对齐可能性,函数F常见方法如下图所示:然后使用Softmax函数进行数值归一化处理。如对“对齐概率”不理解的朋友,可以查看下图英语-德语翻译系统中加入Attention机制后,Encoder和Decoder两个句子中每个单词对应注意力分配概率分布。Self Attention模型在Soft Attention模型中,Attention机制发生在Decoder中Yi和Encoder中的所有元素之间。Self Attention模型不是在两者之间,而是Decoder内部元素之间或者Encoder内部元素之间发生的Attention机制,计算方法和Soft Attention模型一致。那么Self Attention模型有什么好处?我们依然以机器翻译为例:如图所示,Self Attention模型在内部可以捕获一些句法特征或语义特征。Self Attention模型相比传统RNN模型需要依次序序列计算,它的感受野更大,可以直接将句子中的任意两个单词的联系通过一个计算步骤联系起来,可以捕获远距离的相互依赖特征(就像列表和数组的区别)。此外,Self Attention模型对于增加计算的并行性也有帮助。案例我们使用的语言数据集是“英语-西班牙语”,数据集样本如下图所示:数据导入# 数据下载path_to_zip=tf.keras.utils.get_file( fname=‘spa-eng.zip’, origin=‘http://download.tensorflow.org/data/spa-eng.zip', # 解压tar zip文件 extract=True)path_to_file=os.path.dirname(path_to_zip)+’/spa-eng/spa.txt’转码:def unicode_to_ascii(sen): return ‘’.join( char for char in unicodedata.normalize(‘NFD’,sen) if unicodedata.category(char) != ‘Mn’ )数据预处理每条训练语句添加开始和结束标记移除句子中的特殊字符字符转ID,ID转字符并排序将句子补长到预设的最大长度def preprocess_sentence(w): w = unicode_to_ascii(w.lower().strip()) # 在单词和标点之间创建空格 # 如: “he is a boy.” => “he is a boy .” w = re.sub(r"([?.!,¿])", r" \1 “, w) w = re.sub(r’[” “]+’, " “, w) # 特殊字符以空格代替 w = re.sub(r”[^a-zA-Z?.!,¿]+”, " “, w) w = w.rstrip().strip() # 添加开始和结束标记 w = ‘<start> ’ + w + ’ <end>’ return w创建数据集:def create_dataset(path, num_examples): lines = open(path, encoding=‘UTF-8’).read().strip().split(’\n’) word_pairs = [[preprocess_sentence(w) for w in l.split(’\t’)] for l in lines[:num_examples]] # 返回格式:[ENGLISH, SPANISH] return word_pairs字符转ID,ID转字符,并排序:class LanguageIndex(): def init(self,lang): self.lang=lang self.wrod2idx={} self.id2word={} self.vacab=set() self.create_index() def create_index(self): for phrase in self.lang: # 添加到集合中,重复内容不添加 self.vacab.update(phrase.split(’ ‘)) self.vacab=sorted(self.vacab) self.wrod2idx[’<pad>’]=0 #字符-ID转换 for index,word in enumerate(self.vacab): self.wrod2idx[word]=index+1 for word,index in self.wrod2idx.items(): self.id2word[index]=word加载数据集:# 计算最大长度def max_length(tensor): return max(len(t) for t in tensor)def load_dataset(path,num_example): #get inputs outputs pairs=create_dataset(path,num_example) # 获取ID表示 inp_lang=LanguageIndex(sp for en,sp in pairs) targ_lang=LanguageIndex(en for en,sp in pairs) # LanguageIndex 不包含重复值,以下包含重复值 input_tensor=[[inp_lang.wrod2idx[s]for s in sp.split(’ ‘)]for en,sp in pairs] target_tensor=[[targ_lang.wrod2idx[s]for s in en.split(’ ‘)]for en,sp in pairs] max_length_inp,max_length_tar=max_length(input_tensor),max_length(target_tensor) # 将句子补长到预设的最大长度 # padding: post:后补长,pre:前补长 input_tensor=tf.keras.preprocessing.sequence.pad_sequences( sequences=input_tensor, maxlen=max_length_inp, padding=‘post’ ) target_tensor=tf.keras.preprocessing.sequence.pad_sequences( sequences=target_tensor, maxlen=max_length_tar, padding=‘post’ ) return input_tensor,target_tensor,inp_lang,targ_lang,max_length_inp,max_length_tar创建训练集验证集:# 本次项目只使用前30000条数据num_examples = 30000input_tensor, target_tensor, inp_lang, targ_lang, max_length_inp, max_length_targ = load_dataset(path_to_file, num_examples)# 训练集80%,验证集20%input_tensor_train, input_tensor_val, target_tensor_train, target_tensor_val = train_test_split(input_tensor, target_tensor, test_size=0.2)模型训练配置# 打乱数据集BUFFER_SIZE=len(input_tensor_train)BATCH_SIZE=64# 每个epoch迭代次数N_BATCH=BUFFER_SIZE // BATCH_SIZE# 词嵌入维度embedding_dim=256# 隐藏神经元数量units=1024vocab_inp_size=len(inp_lang.wrod2idx)vocab_tar_size=len(targ_lang.wrod2idx)dataset=tf.data.Dataset.from_tensor_slices((input_tensor_train,target_tensor_train)).shuffle(BUFFER_SIZE)# drop_remainder 当剩余数据量小于batch_size时候,是否丢弃dataset=dataset.batch(BATCH_SIZE,drop_remainder=‘True’)案例Attention模型计算文章开始我们介绍了Attention模型的计算过程,相信你会很容易理解上图的内容。对每个节点具体方程实现如下:FC=全连接层,EO=编码器输出,H=隐藏层状态,X=解码器输入,模型计算过程如下表示:score = FC(tanh(FC(EO) + FC(H)))attention weights = softmax(score, axis = 1)context vector = sum(attention weights * EO, axis = 1)embedding output=解码器输入X,输入词嵌入层merged vector=concat(embedding output, context vector)将merged vector输入到GRU创建模型GRU配置:def gru(units): # 使用GPU加速运算 if tf.test.is_gpu_available(): return tf.keras.layers.CuDNNGRU(units, return_sequences=True, return_state=True, # 循环核的初始化方法 # glorot_uniform是sqrt(2 / (fan_in + fan_out))的正态分布产生 # 其中fan_in和fan_out是权重张量的扇入扇出(即输入和输出单元数目) recurrent_initializer=‘glorot_uniform’) else: return tf.keras.layers.GRU(units, return_sequences=True, return_state=True, # hard_sigmoid <= -1 输出0,>=1 输出1 ,中间为线性 recurrent_activation=‘sigmoid’, recurrent_initializer=‘glorot_uniform’)编码器:class Encoder(tf.keras.Model): def init(self, vocab_size, embedding_dim, enc_units, batch_sz): super(Encoder, self).init() self.batch_sz = batch_sz self.enc_units = enc_units self.embedding = tf.keras.layers.Embedding(vocab_size, embedding_dim) self.gru = gru(self.enc_units) def call(self, x, hidden): x = self.embedding(x) output, state = self.gru(x, initial_state = hidden) return output, state def initialize_hidden_state(self): return tf.zeros((self.batch_sz, self.enc_units))解码器:class Decoder(tf.keras.Model): def init(self,vocab_size,embedding_dim,dec_units,batch_sz): super(Decoder, self).init() self.batch_sz=batch_sz self.dec_units=dec_units self.embedding=tf.keras.layers.Embedding( input_shape=vocab_size, output_dim=embedding_dim ) self.gru=gru(self.dec_units) self.fc=tf.keras.layers.Dense(units=vocab_size) # 用于计算score,即:注意力权重系数 self.W1=tf.keras.layers.Dense(self.dec_units) self.W2=tf.keras.layers.Dense(self.dec_units) self.V=tf.keras.layers.Dense(units=1) def call(self,x,hidden,ec_output): # tf.expand_dims:在指定索引出增加一维度,值为1,从索引0开始 # axis: 取值范围是[-阶数,阶数],二维的时候0指的是列,1指的是行, # 更高维度的时候,数值是由外向里增加,如:3维向量,外向内依次是:0,1,2 # 通过计算score公式可得,需要将hidden维度扩展至:[batch_size,1,hidden_size] hidden_with_time_axis=tf.expand_dims(hidden,axis=1) # score=[batch_size, max_length, 1] score=self.V(tf.nn.tanh(self.W1(ec_output)+self.W2(hidden_with_time_axis))) # 数值归一化和为1的概率分布值 attention_weight=tf.nn.softmax(score,axis=1) context_vetor=attention_weightec_output # 求和平均 context_vetor=tf.reduce_sum(context_vetor,axis=1) X=self.embedding(x) # 合并解码器embedding输出和context vector x = tf.concat([tf.expand_dims(context_vector, 1), x], axis=-1) # output shape=(batch_size,time_step,hidden_size) # state shape=(batch_size,hidden_size) output,state=self.gru(x) # output[batch_size1,hidden_size] output=tf.reshape(output,shape=(-1,output.shape[2])) x-self.fc(output) return x,state,attention_weight def initilize_hidden_size(self): return tf.zeros((self.batch_sz,self.dec_units))实例化模型:encoder = Encoder(vocab_inp_size, embedding_dim, units, BATCH_SIZE)decoder = Decoder(vocab_tar_size, embedding_dim, units, BATCH_SIZE)损失函数,优化器:optimizer = tf.train.AdamOptimizer()def loss_function(real, pred): mask = 1 - np.equal(real, 0) loss_ = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=real, logits=pred) * mask return tf.reduce_mean(loss_)模型保存:checkpoint_dir = ‘./training_checkpoints’checkpoint_prefix = os.path.join(checkpoint_dir, “ckpt”)checkpoint = tf.train.Checkpoint(optimizer=optimizer, encoder=encoder, decoder=decoder)训练由于我们使用Teacher Forcing进行训练,所以我们简单介绍下。如图所示Teacher Forcing与Free-running不同,在训练过程中不再是前一时刻的hidden-state作为当前输入,而是在Ground Truth中找到对应的上一项作为当前输入。早期的RNN很弱,如果生成了非常差的结果Free-running的运行方式会导致后面的hidden-state都受到影响。Teacher Forcing运行方式就可以避免这种问题,缺点也很明显它严重依赖标签数据。# 迭代10次训练集EPOCHS = 10for epoch in range(EPOCHS): start = time.time() hidden = encoder.initialize_hidden_state() total_loss = 0 for (batch, (inp, targ)) in enumerate(dataset): loss = 0 # 先记录梯度 with tf.GradientTape() as tape: # 编码器输出 enc_output, enc_hidden = encoder(inp, hidden) dec_hidden = enc_hidden dec_input = tf.expand_dims([targ_lang.word2idx[’<start>’]] * BATCH_SIZE, 1) # 使用Teacher forcing运行方式 for t in range(1, targ.shape[1]): # 解码器输出 predictions, dec_hidden, _ = decoder(dec_input, dec_hidden, enc_output) loss += loss_function(targ[:, t], predictions) # 样本标签作为输入 dec_input = tf.expand_dims(targ[:, t], 1) batch_loss = (loss / int(targ.shape[1])) # one_loss++;batch_loss++ total_loss += batch_loss variables = encoder.variables + decoder.variables gradients = tape.gradient(loss, variables) optimizer.apply_gradients(zip(gradients, variables)) if batch % 100 == 0: print(‘Epoch {} Batch {} Loss {:.4f}’.format(epoch + 1, batch, batch_loss.numpy())) # 每迭代2次训练集保存一次模型 if (epoch + 1) % 2 == 0: checkpoint.save(file_prefix = checkpoint_prefix)翻译评估函数我们不使用teacher-forcing模式,解码器的每步输入是它前一时刻的hidden-state和编码器输出,当模型遇到 <end>标记停止运行。# 和训练模型函数代码基本一致def evaluate(sentence, encoder, decoder, inp_lang, targ_lang, max_length_inp, max_length_targ): attention_plot = np.zeros((max_length_targ, max_length_inp)) # 数据预处理 sentence = preprocess_sentence(sentence) # 向量化表示输入数据 inputs = [inp_lang.word2idx[i] for i in sentence.split(’ ‘)] # 后置补长 inputs = tf.keras.preprocessing.sequence.pad_sequences([inputs], maxlen=max_length_inp, padding=‘post’) inputs = tf.convert_to_tensor(inputs) result = ’’ hidden = [tf.zeros((1, units))] enc_out, enc_hidden = encoder(inputs, hidden) dec_hidden = enc_hidden # 维度扩展batch_size dec_input = tf.expand_dims([targ_lang.word2idx[’<start>’]], 0) for t in range(max_length_targ): predictions, dec_hidden, attention_weights = decoder(dec_input, dec_hidden, enc_out) # 保存权重用于稍后可视化展示 attention_weights = tf.reshape(attention_weights, (-1, )) attention_plot[t] = attention_weights.numpy() predicted_id = tf.argmax(predictions[0]).numpy() # 获取文本翻译结果 result += targ_lang.idx2word[predicted_id] + ’ ’ # 预设的结束标记 if targ_lang.idx2word[predicted_id] == ‘<end>’: return result, sentence, attention_plot # 预测值作为输入,以此输出下一时刻单词 dec_input = tf.expand_dims([predicted_id], 0) return result, sentence, attention_plot可视化权重值: fig = plt.figure(figsize=(10,10)) ax = fig.add_subplot(1, 1, 1) ax.matshow(attention, cmap=‘viridis’) fontdict = {‘fontsize’: 14} ax.set_xticklabels([’’] + sentence, fontdict=fontdict, rotation=90) ax.set_yticklabels([’’] + predicted_sentence, fontdict=fontdict) plt.show()总结本篇文章篇幅较多,不过项目的重点是Attention思想的理解,Self Attention模型具有更长的感受野,更容易捕获长距离的相互依赖特征,目前Google机器翻译模型就大量使用到Self Attention。Attention模型目前在机器翻译,图片描述任务,语音识别都有大量应用,熟练使用Attention对于解决实际问题会有很大的帮助。文章部分内容参考 Yash Katariya 和 张俊林,在此表示感谢。 ...

December 8, 2018 · 4 min · jiezi