Auswahl einer Programmiersprache: 3 Tipps von einem Programmierer von Apple

Bild

Von einem Übersetzer: Dieser Artikel ist eine Übersetzung von Material, das vom Programmierer Alastair Paragas von Apple geschrieben wurde und mit Programmiersprachen wie Javascript, Python, PHP, Java, Scala, Haskell, Swift und Rust gearbeitet hat. Alastair teilt seine Gedanken zum Thema der Auswahl und des Lernens „seiner“ Sprache, da dieses Problem sowohl für Anfänger als auch für Profis relevant ist, die ein neues Toolkit auswählen möchten.

Egal, ob Sie eine Programmiersprache für eine Anstellung oder eine Fortbildung lernen oder es sich um ein reines Hobby handelt, früher oder später müssen Sie sich zwischen ihnen entscheiden. Wie kann man das machen? Die Frage ist nicht einfach, aber die Antwort lautet: Tausende von Programmierern tun es jeden Tag. Um Ihre Aufgabe zu erleichtern, sollten Sie einige Grundsätze befolgen.

Skillbox empfiehlt: Praktikum "Profession Web Developer" .
Wir erinnern Sie daran: Für alle Leser von Habr - ein Rabatt von 10.000 Rubel bei der Anmeldung für einen Skillbox-Kurs gemäß dem Habr-Aktionscode
.

Vergleichsindikatoren


Abstraktionsebenen

Wenn stark verallgemeinert, können wir sagen, dass moderne Programmiersprachen in drei Typen unterteilt sind:

  1. "Schnell", mit denen schnell Anwendungen oder deren Prototypen erstellt werden.
  2. "Infrastruktur", mit deren Hilfe Teile einer bereits geschriebenen Anwendung optimiert oder geändert werden können, um ihre Produktivität zu steigern.
  3. Die sogenannten Systemprogrammiersprachen, mit deren Hilfe Sie die vollständige Kontrolle über den Gerätespeicher erhalten.

Natürlich ist die tatsächliche Trennung der Typen zwischen den Programmiersprachen weniger streng: Es gibt intermediäre Hybridvarianten verschiedener Typen.

Wenn wir über das Erlernen von Sprachen sprechen, sollten Sie zuerst den ersten Typ ausprobieren - „schnelle“ Sprachen: Sie ermöglichen es Ihnen, das Ergebnis der Arbeit sofort zu sehen und aus Ihren eigenen Fehlern zu lernen. Dies sind hauptsächlich PHP, Javascript, Ruby und Python. Die Eintrittsschwelle ist hier minimal und Sie können die Grundlagen in kurzer Zeit erlernen. Diese Sprachen verfügen über Standardbibliotheken, mit denen Sie der Anwendung eine große Anzahl von Funktionen hinzufügen können, und der Umfang ihrer Funktionen ist recht groß.

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) 

Implementieren von HTTP-Anforderungen mit mehreren Threads in Python mit statischer Typisierung. Multithreading bietet die Möglichkeit, drei Aufgaben zu wechseln (nennen wir sie Aufgaben A, B und C). Während eine Aufgabe (z. B. Aufgabe A) eine Operation im Zusammenhang mit E / A ausführt (und daher keine Berechnungsarbeit ausführt), werden andere Aufgaben gleichzeitig damit ausgeführt.

Bei den "Infrastruktur" -Sprachen handelt es sich um Java, Kotlin, Scala, Clojure sowie GoLang, Swift und Haskell. Sie können sie bequem mit einer Strecke aufrufen, aber Sie können damit produktive Anwendungen erstellen. Zu den Komplexitäten gehören weniger sofort einsatzbereite Elemente, eine präzise Syntax usw. Diese Sprachen sind gut, weil Sie damit die Anwendung optimieren können. Wenn Sie Geschwindigkeit benötigen, schreiben Sie eine Anwendung auf eine von ihnen.

 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() 

Ein ähnliches Problem wurde bereits oben mit Python gelöst. Jetzt im Geschäft - Swift.

Systemprogrammiersprachen - C, C ++, Rust. Sie bieten maximale Kontrolle über die Anwendung, einschließlich der Speicherverwaltung. Diese Sprachen eignen sich auch hervorragend zum Programmieren von Mikrocontrollern, Computern mit benutzerdefinierter Prozessorarchitektur und anderen Systemen. Niedrigsprachige Sprachen sind nach wie vor wichtig und werden höchstwahrscheinlich in naher Zukunft relevant bleiben.

Funktionalität

Wie Sie wissen, dienen Sprachen als Mittel zur "Kommunikation" zwischen einem Computer und einem Programmierer. Damit diese Kommunikation reibungslos verläuft, lohnt es sich, die Syntax der Sprache im Detail zu studieren. Insbesondere sollte ein Spezialist die am häufigsten verwendeten Datenstrukturen kennen und verstehen, wie bestimmte Elemente seiner Anwendung geändert werden.

 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 ist eine streng funktionale Programmiersprache. Er kann eingehende Datenstrukturen überprüfen und mit ihnen arbeiten, wenn sie bestimmte Anforderungen erfüllen.

Laufzeit - Sie müssen wissen, wie Ihre Anwendung auf verschiedenen Systemen funktioniert. Wird ein Sprachinterpreter benötigt (z. B. Python, NodeJS, PHP)? Wird eine systemabhängige Binärdatei generiert (z. B. Swift und GoLang)? Verwendet die ausgewählte Sprache eine Kombination aus der ersten und der zweiten Option, z. B. wird die Anwendung auf einer virtuellen Maschine (Java, Scala, Clojure) kompiliert und gestartet?
Übrigens wird auf dem Weg zu Spitzenleistungen dringend empfohlen, Docker plus zu studieren und zu verwenden, um die Linux-Administrationsprinzipien zu verstehen.

Bibliotheken - jede Sprache ist in bestimmten Situationen gut geeignet. Beispielsweise erfüllt Java viele der Anforderungen der Orchestrierung und Netzwerklogistik, einschließlich der Datenbankunterstützung durch Standardisierung der JDBC-Schnittstelle und von Projekten, wie sie von der Apache Foundation unterstützt werden. Gleiches gilt für Python - ideal für Datenanalysen und statistische Berechnungen - sowie für Haskell mit seinen Grammatiken, regulären Ausdrücken und Compilern. Die Popularität der Sprache und die Größe ihrer Community sind zwei weitere Argumente, die für die Verwendung eines bestimmten Programmierwerkzeugs in Ihrem Projekt sprechen. Wenn die Gemeinde klein ist, sollten Sie nicht auf die Ambulanzhilfe der Teilnehmer zählen. Und umgekehrt: Je größer die Community und je beliebter die Programmiersprache, desto schneller können Sie eine schwierige Aufgabe lösen oder sich von Kollegen beraten lassen.

Müllabfuhr

Die Speicherbereinigung ist eine Form der automatischen Speicherverwaltung. Ein spezieller Prozess namens Garbage Collector gibt regelmäßig Speicher frei, indem Objekte gelöscht werden, die von Anwendungen nicht mehr benötigt werden. Jede Programmiersprache tut dies auf ihre eigene Weise.

Python implementiert die Referenzzählung über den Stop-the-World-Algorithmus. Es unterbricht die Programmausführung, startet und führt die Speicherbereinigung aus und setzt dann den Hauptprozess fort. Während der „Reinigung“ erscheinen 3 separate „Generationen“ - eine Reihe von „Müllhaufen“. Null enthält die „frischesten“ Objekte, dann folgen die Generationen 1 und 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 

Implementierung des Python-Garbage-Collectors


Die Ergebnisse des obigen Programmcodes

PHP (ab Version PHP5.3) verwendet neben der Referenzzählung eine weitere Garbage Collection-Option. Hier wird dieser Vorgang ggf. zusammen mit dem Programm durchgeführt. Untergraphen, die von der Wurzel aus nicht erreichbar sind, werden entfernt.

Swift verwendet auch die Referenzzählung. Es gibt keine anderen Möglichkeiten, Müll zu sammeln. Der Fall wird unten gezeigt, wenn der "starke" Zähler des Objekts vollständig 0 erreicht und Person löscht (da er schwach mit Wohnung korreliert).

 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") } 



Es gibt viele Beispiele für Garbage Collection-Mechanismen, die in anderen Sprachen implementiert sind. Sie können die Gesamtleistung der Anwendung oder der Arbeit beeinträchtigen, ohne die Ausführung der Hauptaufgabe zu beeinträchtigen.

Doppelte Konzepte


Pakete erstellen und verwalten

Machen Sie sich mit den Mechanismen zum Speichern und Verfolgen von Abhängigkeiten sowie mit Möglichkeiten zum Verwalten von Informationen über die "Assembly" vertraut (Beschreibung des Pakets, Ausführen von Komponententests, Konfigurieren und Vorbereiten der Umgebung usw.).

Python verwendet pip zusammen mit einer Datei " require.txt" zum Verwalten von Abhängigkeiten und setup.py zum Verwalten von Umgebungseinstellungen . Haskell arbeitet mit Cabal zusammen , um beide Probleme zu lösen. Java hat Maven und Gradle . Im Fall von Scala SBT funktioniert PHP Composer , NodeJS - npm usw.

Stellen Sie sicher, dass Sie sich für die Lokalisierung der Entwicklungsumgebung entscheiden. Je nach Projekt möchten Sie möglicherweise verschiedene Versionen von Programmiersprachen ausführen. Phpbrew für PHP, pyenv für Python und nvm für NodeJS machen dies möglich.


Mit pyenv können Sie mit verschiedenen Versionen von Python arbeiten.

In besonderen Fällen kommt es vor, dass die in einem Projekt verwendete Bibliothek automatisch in einem anderen installiert wird. Dies gilt insbesondere für Sprachen wie Python und Haskell. Um dieses Problem zu vermeiden, sollten Sie virtualenv / venv für Python, virtphp für PHP und Cabal Sandboxes für Haskell verwenden.



Asynchrone Ein- / Ausgabe

Dies ist eine Gelegenheit, die E / A-Leistung von Anwendungsdaten zu erhöhen. Gleichzeitig arbeitet jeder Thread mit einem eigenen Satz von Registern und Stapelinformationen.



 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"); 

Implementieren asynchroner E / A mit Javascript

Funktionsprogrammierung

Mit der funktionalen Programmierung können Sie dem Computer auf hoher Ebene "sagen", was Sie wirklich von ihm wollen. Die meisten Sprachen verfügen heutzutage über die grundlegendsten Funktionen, um dies zu implementieren: durch Zuordnen , Filtern , Reduzieren für Listen usw. Aber es lohnt sich immer noch. Nachfolgend finden Sie ein Beispiel für eine funktionale Programmierung in einer Sprache, die eine solche Möglichkeit nicht zu implizieren scheint.

 <?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"; 

Schulung


Die erste Phase ist die Suche nach den erforderlichen Informationen zu speziellen Ressourcen und die Erstellung eines kleinen Projekts nach Abschluss der Grundausbildung. In den meisten Fällen können Sie Artikel wie „X in Y-Tagen lernen“ verwenden, von denen viele sehr gut sind. In vielen Fällen gibt es interaktive Trainingsbeispiele: Eine Tour durch GoLang und GoLang anhand eines Beispiels (für GoLang), NodeSchool-Befehlszeilenübungen (für Javascript, nämlich NodeJS), Scala-Übungen (für Scala), Python Koans (für Python) usw. .p.

Mit etwas Kompliziertem zu beginnen, lohnt sich nicht. Das Erstellen kleiner Anwendungen und Skripte ist das, was ein Anfänger benötigt. Die Gesamtzahl der Codezeilen in solchen Experimenten überschreitet 300–400 nicht. Die Hauptsache, die in dieser Phase notwendig ist, ist, grundlegende Informationen zu erhalten, das Programmieren mit jeder normalen Geschwindigkeit zu lernen und das Wichtigste ist zu verstehen, was Sie tun.

 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") } } 

Ein Beispiel für ein erstes Skript, das einem unerfahrenen Programmierer eine Vorstellung davon gibt, wie sein Code funktioniert

Die zweite Phase ist ein tieferes Studium der Sprache, die Schaffung eines vollwertigen Projekts, das nicht mehr als "Kinder" bezeichnet werden kann. In vielen Fällen müssen Sie sich mit der offiziellen Dokumentation vertraut machen. Für Javascript sind es Mozilla Developer Docs , für Swift, Swift Official Docs , für Java, Java Learning Trails , für Python, Python Official Docs t. Besondere Aufmerksamkeit sollte Online-Kursen mit guten Lehrern gewidmet werden.

Es lohnt sich auch, andere Open Source-Projekte zu erkunden. Ressourcen wie Annotated jQuery Source oder Annotated BackboneJS Source geben eine Vorstellung davon, wie eine bestimmte Programmiersprache und zusätzliche Bibliotheken in professionellen Projekten verwendet werden.

All dies hilft Ihnen dabei, Ihr eigenes ernstes Projekt zu erstellen, z. B. eine Desktop-Anwendung, eine Webanwendung oder ein mobiles Programm. Versuchen Sie, externe Bibliotheken zu verwenden, wenn Sie zusätzliche Tools und Funktionen benötigen.



Vergessen Sie nicht die Leistung Ihrer Anwendung, versuchen Sie immer, Informationen aus den neuesten Quellen zu beziehen. Die Lernmöglichkeiten sind endlos, Sie können sich für immer verbessern. Aber am Ende kann man sich von Anfang an als Profi fühlen - und es gibt einfach kein besseres Gefühl auf der Welt.
Skillbox empfiehlt:

Source: https://habr.com/ru/post/de421533/


All Articles