选择一种编程语言:Apple程序员的3个技巧

图片

译者:本文是苹果程序员Alastair Paragas编写的材料的翻译,并且使用Java,Python,PHP,Java,Scala,Haskell,Swift和Rust等编程语言。 Alastair在选择和学习“他的”语言这一主题上分享了他的想法,因为此问题与希望选择新工具包的初学者和专业人员都相关。

无论您是为了就业还是接受高级培训而学习编程语言,还是纯粹的业余爱好,迟早都要在它们之间进行选择。 怎么做? 问题并不简单,但答案是:每天都有成千上万的程序员这样做。 为了简化您的任务,值得遵循一些原则。

Skillbox建议:实用课程“专业Web开发人员”
我们提醒您:对于所有Habr读者-根据Habr促销代码注册任何Skillbox课程时均可享受10,000卢布的折扣

比较指标


抽象层次

如果大力概括,我们可以说现代编程语言分为三种类型:

  1. “快速”,用于快速创建应用程序或其原型。
  2. “基础结构”,可帮助优化或修改已编写应用程序的部分,以提高其生产率。
  3. 所谓的系统编程语言,使您可以完全控制设备的内存。

当然,在编程语言之间对类型进行真正的分隔并不那么严格:存在各种类型的中间混合变体。

如果我们谈论学习语言,那么首先您应该尝试第一类-“快速”语言:它们使您可以立即看到工作的结果并从自己的错误中学习。 这些主要是PHP,Javascript,Ruby和Python。 此处的入门门槛极低,您可以在短时间内学习基础知识。 这些语言具有标准库,可让您向应用程序添加大量功能,并且它们的功能范围也很大。

from concurrent.futures import ThreadPoolExecutor from http.client import HTTPException from urllib import request from typing import Union, Dict, Any, List def get_request_task(url: str) -> Union[List[Dict[str, Any]], None]: try: contents = None with request.urlopen(url) as response: contents = response.read() return contents except HTTPException: return None with ThreadPoolExecutor() as executor: for result in executor.map(get_request_task, [ "https://jsonplaceholder.typicode.com/posts", "https://jsonplaceholder.typicode.com/comments", "https://jsonplaceholder.typicode.com/albums" ]): if result is None: print("Something terrible has happened!") else: print(result) 

使用静态类型在Python中实现多线程HTTP请求。 多线程提供了交替执行三个任务的可能性(我们将其称为任务A,B和C)。 当一个任务(例如任务A)执行与I / O相关的某些操作(因此不执行任何计算工作)时,其他任务则与之同时执行。

至于“基础设施”语言,它们是Java,Kotlin,Scala,Clojure以及GoLang,Swift和Haskell。 您可以轻松地称它们为方便,但是它们允许您创建高效的应用程序。 复杂性包括更少的现成元素,精确的语法等。 这些语言很好,因为它们使您可以微调应用程序。 如果需要速度,请尝试在其中之一上编写应用程序。

 import Foundation import Dispatch func getRequestTask(url: String, dispatchGroup: DispatchGroup) { dispatchGroup.enter() let request = URLRequest(url: URL(string: url)!) let task = URLSession(configuration: URLSessionConfiguration.default).dataTask( with: request, completionHandler: { (data, response, error) in if let data = data { if let dataAsString = String(data: data, encoding: .utf8) { print(dataAsString) dispatchGroup.leave() return } } print("Something terrible has happened!") dispatchGroup.leave() } ) task.resume() } let requestDispatchGroup = DispatchGroup() for url in [ "https://jsonplaceholder.typicode.com/posts", "https://jsonplaceholder.typicode.com/comments", "https://jsonplaceholder.typicode.com/albums" ] { getRequestTask(url: url, dispatchGroup: requestDispatchGroup) } requestDispatchGroup.wait() 

上面使用Python解决了类似的问题。 现在开始营业-Swift。

系统编程语言-C,C ++,Rust。 它们提供了对应用程序的最大控制,包括内存管理。 同样,这些语言非常适合对微控制器,具有自定义处理器体系结构的计算机和其他系统进行编程。 低级语言仍然很重要,并且很可能在不久的将来仍将保持相关性。

功能性

众所周知,语言是计算机与程序员之间“交流”的一种手段。 为了使这种交流顺利进行,值得详细研究该语言的语法。 特别是,专家应该了解最常用的数据结构,并了解如何修改其应用程序的某些元素。

 module Main where import Control.Monad.IO.Class (liftIO) import Control.Monad.Trans.Resource (runResourceT) import Data.Conduit (($$+-), ($=+), runConduit) import Data.Conduit.List (mapM_, map, filter, catMaybes) import Data.Text (unpack) import Data.Maybe (fromJust) import Web.Twitter.Types (StreamingAPI(SStatus, SRetweetedStatus) , Status(Status), statusText, statusLang , RetweetedStatus(RetweetedStatus), rsRetweetedStatus ) import Web.Twitter.Conduit.Stream (stream) -- Filters Twitter tweets that are written only in English filterEnglishTweets :: StreamingAPI -> Bool filterEnglishTweets tweet = let langIsEnglish (Status {statusLang=language}) = case language of Just "en" -> True _ -> False in case tweet of SStatus statusObj -> langIsEnglish statusObj SRetweetedStatus (RetweetedStatus {rsRetweetedStatus=statusObj}) -> langIsEnglish statusObj _ -> False -- Filters Twitter tweets that are original posts tweetParser :: StreamingAPI -> Maybe String tweetParser tweet = case tweet of SStatus (Status {statusText=status}) -> Just $ unpack status SRetweetedStatus (RetweetedStatus {rsRetweetedStatus=rstatus}) -> Just $ unpack $ statusText rstatus _ -> Nothing main :: IO () main = do -- a bunch of connection setup details to Twitter {- Imagine a stream/production line of continually incoming Twitter tweets out of this stream, non-English tweets are removed each remaining tweet then gets packaged into one of two forms - one for original tweets - one for non-original tweets (retweets and whatnot) We then only grab packaged forms of original tweets and display them! -} in runResourceT $ do stream <- stream twitterInfo connectionManager apiRequest stream $=+ Data.Conduit.List.filter filterEnglishTweets $=+ Data.Conduit.List.map tweetParser $=+ Data.Conduit.List.catMaybes $$+- Data.Conduit.List.mapM_ (liftIO . putStrLn) 

Haskell是一种严格的函数式编程语言。 他可以检查传入的数据结构,并在满足某些要求的情况下使用它们。

运行时 -您需要知道您的应用程序如何在各种系统上运行。 是否需要语言解释器(例如Python,NodeJS,PHP)? 是否生成依赖于系统的二进制文件(例如Swift和GoLang)? 所选语言是否使用第一选项和第二选项的组合,例如,应用程序是在某些虚拟机(Java,Scala,Clojure)上编译并启动的?
顺便说一句,在追求卓越的道路上,强烈建议学习并开始使用Docker plus,以确保了解Linux管理原则。

图书馆 -每种语言都非常适合特定情况。 例如,Java满足了编排和网络物流的许多要求,包括通过JDBC接口和诸如Apache Foundation支持的项目的标准化对数据库的支持。 Python同样适用-非常适合数据分析和统计计算-以及Haskell及其语法,正则表达式和编译器。 语言的流行及其社区的规模是另外两个争论,它们主张在项目中使用某种编程工具。 如果社区很小,那么您不应该依靠其参与者的救护车帮助。 反之亦然,社区越大,编程语言越流行,您可以更快地解决难题或获得同事的建议。

垃圾收集

垃圾回收是自动内存管理的一种形式。 一个称为垃圾收集器的特殊进程会通过删除应用程序不再需要的对象来定期释放内存。 每种编程语言都以自己的方式执行此操作。

Python通过世界停止算法实现引用计数。 它暂停程序执行,启动并执行垃圾回收,然后继续执行主进程。 在“清洁”期间,出现了3个独立的“世代”-一组“垃圾堆”。 零包含“最新鲜”的对象,然后是第1代和第2代。

 import gc import ctypes gc.set_debug(gc.DEBUG_SAVEALL) class PyObject(ctypes.Structure): _fields_ = [("refcnt", ctypes.c_long)] object1 = {} object2 = {} object3 = {} object1['reference_to_2'] = object2 object2['reference_to_1'] = object1 object3['some_key'] = 1 object1_memory_address = id(object1) object2_memory_address = id(object2) object3_memory_address = id(object3) print "Before garbage collection --->" print "Refcount for object1: {count}".format( count=PyObject.from_address(object1_memory_address).refcnt ) print "Refcount for object2: {count}".format( count=PyObject.from_address(object2_memory_address).refcnt ) print "Refcount for object3: {count}".format( count=PyObject.from_address(object3_memory_address).refcnt ) del object1, object2, object3 gc.collect() print "After garbage collection --->" print "Refcount for object1: {count}".format( count=PyObject.from_address(object1_memory_address).refcnt ) print "Refcount for object2: {count}".format( count=PyObject.from_address(object2_memory_address).refcnt ) print "Refcount for object3: {count}".format( count=PyObject.from_address(object3_memory_address).refcnt ) print "Objects that cannot be cleaned up by reference counting: --->" for x in gc.garbage: print x 

Python垃圾收集器实现


上面程序代码的结果

PHP(从版本PHP5.3开始)使用了另一个垃圾回收选项以及引用计数。 在此,如有必要,该过程与程序一起执行。 从根无法到达的子图被消除。

Swift还使用引用计数;没有其他方法可以收集垃圾。 当对象的“强”计数器完全达到0并清除Person(由于它与Apartment的关联性较弱)时,情况如下所示。

 class Person { let name: String init(name: String) { self.name = name } var apartment: Apartment? deinit { print("\(name) is being deinitialized") } } class Apartment { let unit: String init(unit: String) { self.unit = unit } weak var tenant: Person? deinit { print("Apartment \(unit) is being deinitialized") } 



有许多其他语言实现的垃圾收集机制示例。 它们可以影响应用程序或工作的整体性能,而不会影响主要任务的执行。

重复的概念


创建和管理软件包

熟悉存储和跟踪依赖项的机制,以及维护有关“程序集”(程序包的描述,如何运行单元测试,配置和准备环境等)信息的方法。

Python结合使用pip和requirements.txt文件来管理依赖关系,并使用setup.py来管理环境设置 ,Haskell与Cabal一起解决了这两个问题,Java有MavenGradle ,在Scala SBT的情况下,PHP使用Composer ,NodeJS -npm

确保确定开发环境的本地化-您可能要根据项目运行不同版本的编程语言。 PHP的Phpbrew,Python的pyenv和NodeJS的nvm使其成为可能。


使用pyenv,您可以使用不同版本的Python。

在特殊情况下,一个项目中使用的库可能会自动安装到其他项目中。 对于像Python和Haskell这样的语言尤其如此。 为避免此问题,对于Python应该使用virtualenv / venv ,对于PHP应该使用virtphp ,对于Haskell使用Cabal Sandboxes



异步输入/输出

这是提高应用程序数据的I / O性能的机会。 同时,每个线程都使用自己的一组寄存器和堆栈信息。



 const https = require("https"); const urlList = [ "https://reqres.in/api/users?page=1", "https://reqres.in/api/users?page=2", "https://reqres.in/api/users?page=3" ]; function getSiteContents(url) { return new Promise(function (resolve, reject) { https.get(url, function (res) { var bodyData = ""; res.on("data", function (chunk) { bodyData += chunk; }); res.on("end", function () { resolve(bodyData); }); res.on("error", function (error) { reject(error.message); }); }); }); } // One way we can proceed with execution // Make one Promise out of a list of Promises Promise.all(urlList.map(getSiteContents)) .then(function (siteContents) { console.log("Promise based execution --->"); console.log(siteContents); }); // Another way we can proceed with execution // "async" is an ES7 feature that makes our Promise/async I/O code look // more synchronous async function main () { const siteContents = await Promise.all(urlList.map(getSiteContents)) console.log("Main() based execution --->"); console.log(siteContents); } main(); // As Promises will happen in some future time, this will happen first console.log("This console.log will most likely happen first"); 

使用Java脚本实现异步I / O

功能编程

通过功能编程,您可以从高级角度“讲”计算机您真正想要的是什么。 当今,大多数语言都具有实现此功能的最基本功能:通过mapfilterreduce for list等。 但是仍然值得使用。 以下是使用某种语言的函数式编程示例,该语言似乎并不意味着这种可能性。

 <?php // Accumulator gets passed around for reuse - function as a value $accumulator = function ( string $accumulated_string, string $mapped_list_element ) { return $accumulated_string . $mapped_list_element . "\n"; }; // Notice how array_map, array_filter and array_reduce // accept functions as parameters - they are higher order functions $mapped_array = array_reduce( array_map( function (int $list_element): string { return "A list element: " . $list_element; }, [1, 2, 3, 4] ), $accumulator, "" ); echo "Mapped Array: \n"; echo $mapped_array; $filtered_array = array_reduce( array_filter( [1, 2, 3, 4], function (int $list_element): bool { return $list_element > 2; } ), $accumulator, "" ); echo "Filtered Array: \n"; echo $filtered_array; // Closures "enclose" over their surrounding state // The $closure_incrementer function here returns a function // making it a higher order function. echo "Closure Incrementer: \n"; $closure_incrementer = function () { $internal_variable = 0; return function () use (&$internal_variable) { return $internal_variable += 1; }; }; $instance = $closure_incrementer(); echo $instance() . " is equal to 1\n"; echo $instance() . " is equal to 2\n"; 

培训课程


第一阶段是在基础培训完成后,寻找有关专业资源的必要信息,并创建一个小项目。 在大多数情况下,您可以使用诸如“在Y日内学习X”之类的文章,其中很多都很好。 在很多情况下,都有交互式培训示例: 以GoLang 为例 的GoLangGoLang 游览 (GoLang), NodeSchool命令行练习 (对于Javascript,即NodeJS), Scala练习 (对于Scala), Python Koans(对于Python)等。 .p。

从复杂的事情开始是不值得的。 创建小型应用程序和脚本是初学者所需要的。 在此类实验中,代码的总行数不超过300-400。 在此阶段,最主要的事情是获取基本信息,学习以任何正常速度编程,最重要的是了解您的工作。

 func containedClosureIncrementer() -> (() -> Int) { var anInt = 0 func incrementer() -> Int { anInt = anInt + 1 return anInt } return incrementer } func containedClosureIncrementer2() -> () -> Int { var anInt = 0 return { anInt = anInt + 1 return anInt } } let closureIncrementer = containedClosureIncrementer() print("containedClosureIncrementer call - should be 1: \(closureIncrementer() == 1)") print("containedClosureIncrementer call - should be 2: \(closureIncrementer() == 2)") var someOptionalValue: Optional<String> = nil; print("Optional - someOptionalValue is null: \(someOptionalValue == nil)") someOptionalValue = "real value" print("Optional - someOptionalValue is 'real value' \(someOptionalValue == "real value")") (["real value", nil] as Array<Optional<String>>).forEach({ someOptionalValue in if let someValue = someOptionalValue { if someValue.hasPrefix("real") { print("someValue: has real") } else { print("someValue: doesn't have real") } } else { print("someValue: has nil") } }) if (someOptionalValue ?? "").hasPrefix("real") { print("Has real 2") } else { print("Doesn't have real") } let numbersList: [Int] = Array(1...10) print("List of numbers 1 to 10: \(numbersList)") let numbersListTimes2 = numbersList.map({ (someNumber: Int) -> Int in let multiplicand = 2 return someNumber * multiplicand }) let numbersListTimes2V2 = numbersList.map({ number in number * 2 }) let numbersListTimes2V3 = numbersList.map { $0 * 2 } print("List of numbers * 2: \(numbersListTimes2)") print("V1, V2 Map operations do the same thing: \(numbersListTimes2 == numbersListTimes2V2)") print("V1, V3 Map operations do the same thing: \(numbersListTimes2 == numbersListTimes2V3)") func testGuard() { let someOptionalValue: Optional<String> = nil; guard let someOptionalValueUnwrapped = someOptionalValue else { print("testGuard: Thrown exception - nil value") return } print("testGuard: no exception - non-nil value: \(someOptionalValueUnwrapped)") } testGuard() class RuntimeError: Error {} [{throw RuntimeError()}, {1} as () throws -> Int].forEach { let returnValue = try? $0() if let returnValueUnwrapped = returnValue { print("List of closures: A normal value was returned \(returnValueUnwrapped)") } else { print("List of closures: An error was thrown") } } 

一个初始脚本的示例,该脚本为新手程序员提供了有关其代码工作原理的想法

第二阶段是对语言进行更深入的研究,创建一个成熟的项目,该项目不再被称为“幼稚的”。 在许多情况下,您需要熟悉官方文档。 对于Javascript,它是Mozilla开发人员文档 ,用于Swift, Swift官方文档 ,用于Java, Java Learning Trails ,用于Python, Python官方文档 t。 应特别注意与好老师一起进行的在线课程。

值得探索其他开源项目。 诸如带注释的jQuery源或带注释的BackboneJS源之类的资源可以让您了解如何在专业项目中使用特定的编程语言和其他库。

所有这些都将有助于创建您自己的严肃项目,例如,桌面应用程序,Web应用程序,移动程序。 需要其他工具和功能时,请尝试使用外部库。



不要忘记应用程序的性能,请始终尝试从最新资源中获取信息。 学习机会无止境,您可以永远进步。 但最终,您会觉得自己已经从初学者开始成为专业人士-简直是世上没有更好的感觉。
Skillbox建议:

Source: https://habr.com/ru/post/zh-CN421533/


All Articles