Elegir un lenguaje de programación: 3 consejos de un programador de Apple

imagen

De un traductor: Este artículo es una traducción de material escrito por el programador Alastair Paragas de Apple y ha trabajado con lenguajes de programación como Javascript, Python, PHP, Java, Scala, Haskell, Swift y Rust. Alastair comparte sus pensamientos sobre el tema de elegir y aprender "su" idioma, porque este tema es relevante tanto para principiantes como para profesionales que desean elegir un nuevo juego de herramientas.

Ya sea que aprenda un lenguaje de programación por motivos de empleo o capacitación avanzada, o sea pura afición, tarde o temprano tendrá que elegir entre ellos. Como hacerlo La pregunta no es simple, pero la respuesta es: miles de programadores lo hacen todos los días. Para facilitar su tarea, vale la pena seguir algunos principios.

Skillbox recomienda: Curso práctico "Profession Web Developer" .
Le recordamos: para todos los lectores de Habr: un descuento de 10.000 rublos al registrarse en cualquier curso de Skillbox de acuerdo con el código promocional de Habr
.

Indicadores comparativos


Niveles de abstracción.

Si está muy generalizado, podemos decir que los lenguajes de programación modernos se dividen en tres tipos:

  1. "Rápido", que se utilizan para crear rápidamente aplicaciones o sus prototipos.
  2. "Infraestructura", que ayuda a optimizar o modificar partes de una aplicación ya escrita para aumentar su productividad.
  3. Los llamados lenguajes de programación del sistema, cuyo uso le permite obtener un control completo sobre la memoria del dispositivo.

Por supuesto, la separación real de los tipos entre los lenguajes de programación es menos estricta: existen variantes híbridas intermedias de varios tipos.

Si hablamos de aprender idiomas, entonces primero debe probar el primer tipo: idiomas "rápidos": le permiten ver inmediatamente el resultado del trabajo y aprender de sus propios errores. Estos son principalmente PHP, Javascript, Ruby y Python. El umbral de entrada aquí es mínimo, y puede aprender los conceptos básicos en poco tiempo. Estos lenguajes tienen bibliotecas estándar que le permiten agregar una gran cantidad de funciones a la aplicación, y el rango de sus capacidades es bastante amplio.

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) 

Implementación de solicitudes HTTP multiproceso en Python con escritura estática. Multithreading proporciona la posibilidad de alternar tres tareas (llamémoslas tareas A, B y C). Mientras que una tarea (por ejemplo, la tarea A) realiza alguna operación relacionada con E / S (y, por lo tanto, no realiza ningún trabajo de cálculo), otras tareas se realizan simultáneamente con ella.

En cuanto a los lenguajes de "infraestructura", estos son Java, Kotlin, Scala, Clojure, así como GoLang, Swift y Haskell. Puede llamarlos convenientes con un estiramiento, pero le permiten crear aplicaciones productivas. Las complejidades incluyen menos elementos listos para usar, sintaxis precisa, etc. Estos idiomas son buenos porque le permiten ajustar la aplicación. Si necesita velocidad, intente escribir una aplicación en uno de ellos.

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

Un problema similar ya se ha resuelto anteriormente usando Python. Ahora en el negocio - Swift.

Lenguajes de programación del sistema: C, C ++, Rust. Proporcionan el máximo control sobre la aplicación, incluida la gestión de memoria. Además, estos lenguajes son excelentes para programar microcontroladores, computadoras con arquitectura de procesador personalizada y otros sistemas. Los idiomas de bajo nivel siguen siendo importantes y lo más probable es que sigan siendo relevantes en el futuro cercano.

Funcionalidad

Como saben, los idiomas son un medio de "comunicación" entre una computadora y un programador. Para que esta comunicación sea fluida, vale la pena estudiar la sintaxis del lenguaje en detalle. En particular, un especialista debe conocer las estructuras de datos más utilizadas y comprender cómo modificar ciertos elementos de su aplicación.

 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 es un lenguaje de programación funcional estricto. Puede verificar las estructuras de datos entrantes y trabajar con ellas si cumplen ciertos requisitos.

Tiempo de ejecución : necesita saber cómo funcionará su aplicación en varios sistemas. ¿Se necesita un intérprete de lenguaje (por ejemplo, Python, NodeJS, PHP)? ¿Se genera un binario dependiente del sistema (por ejemplo, Swift y GoLang)? ¿Utiliza el idioma seleccionado una combinación de las opciones primera y segunda, por ejemplo, la aplicación se compila y se inicia en alguna máquina virtual (Java, Scala, Clojure)?
Por cierto, en el camino hacia la excelencia, es muy recomendable estudiar y comenzar a usar Docker plus para asegurarse de comprender los principios de administración de Linux.

Bibliotecas : cada idioma es adecuado en ciertas situaciones. Por ejemplo, Java cumple con muchos de los requisitos de orquestación y logística de red, incluido el soporte de bases de datos a través de la estandarización de la interfaz JDBC y proyectos como los que están bajo el apoyo de la Fundación Apache. Lo mismo ocurre con Python, es ideal para el análisis de datos y los cálculos estadísticos, así como con Haskell con sus gramáticas, expresiones regulares y compiladores. La popularidad del lenguaje y el tamaño de su comunidad son dos argumentos más que hablan a favor del uso de una determinada herramienta de programación en su proyecto. Si la comunidad es pequeña, no debe contar con la asistencia de ambulancia de sus participantes. Y viceversa, cuanto más grande sea la comunidad y más popular sea el lenguaje de programación, más rápido podrá resolver una tarea difícil u obtener consejos de colegas.

Recolección de basura

La recolección de basura es una forma de administración automática de memoria. Un proceso especial llamado recolector de basura periódicamente libera memoria al eliminar objetos que las aplicaciones ya no necesitan. Cada lenguaje de programación hace esto a su manera.

Python implementa el conteo de referencias a través del algoritmo stop-the-world. Pausa la ejecución del programa, inicia y ejecuta la recolección de basura, luego reanuda el proceso principal. Durante la "limpieza" aparecen 3 "generaciones" separadas: un conjunto de "pilas de basura". Zero contiene los objetos "más frescos", luego siguen las generaciones 1 y 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 

Implementación del recolector de basura Python


Los resultados del código del programa anterior

PHP (comenzando con la versión PHP5.3) usa otra opción de recolección de basura junto con el conteo de referencias. Aquí, este proceso, si es necesario, se realiza junto con el programa. Se eliminan los subgráficos que no se pueden alcanzar desde la raíz.

Swift también utiliza el recuento de referencias; no hay otras formas de recolectar basura. El caso se muestra a continuación cuando el contador "fuerte" del objeto llega por completo a 0 y borra Persona (ya que está débilmente correlacionado con Apartamento).

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



Hay muchos ejemplos de mecanismos de recolección de basura implementados en otros idiomas. Pueden afectar el rendimiento general de la aplicación o el trabajo sin afectar la ejecución de la tarea principal.

Conceptos duplicados


Crear y administrar paquetes

Conozca los mecanismos para almacenar y rastrear dependencias, así como las formas de mantener información sobre el "ensamblaje" (descripción del paquete, cómo ejecutar pruebas unitarias, configurar y preparar el entorno, etc.).

Python usa pip en conjunto con un archivo require.txt para administrar las dependencias y setup.py para administrar la configuración del entorno , Haskell trabaja con Cabal para resolver ambos problemas, Java tiene Maven y Gradle , en el caso de Scala SBT funciona, PHP usa Composer , NodeJS - npm , etc.

Asegúrese de decidir la localización del entorno de desarrollo; es posible que desee ejecutar diferentes versiones de lenguajes de programación según el proyecto. Phpbrew para PHP, pyenv para Python y nvm para NodeJS lo hacen posible.


Usando pyenv, puedes trabajar con diferentes versiones de Python.

En casos especiales, sucede que la biblioteca utilizada en un proyecto se instala automáticamente en otros. Esto es cierto, en particular, para lenguajes como Python y Haskell. Para evitar este problema, debe usar virtualenv / venv para Python, virtphp para PHP y Cabal Sandboxes para Haskell.



Entrada / salida asincrónica

Esta es una oportunidad para aumentar el rendimiento de E / S de los datos de la aplicación. Al mismo tiempo, cada hilo funciona con su propio conjunto de registros e información de pila.



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

Implementación de E / S asíncronas usando Javascript

Programación funcional

La programación funcional le permite "decirle" a la computadora a un alto nivel lo que realmente quiere de ella. La mayoría de los idiomas actuales tienen las capacidades más básicas para implementar esto: a través del mapa , filtro , reducción de listas , etc. Pero todavía vale la pena usarlo. A continuación se muestra un ejemplo de programación funcional en un lenguaje que no parece implicar tal posibilidad.

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

Entrenamiento


La primera etapa es la búsqueda de la información necesaria sobre recursos especializados y la creación de un pequeño proyecto después de completar la capacitación básica. En la mayoría de los casos, puede usar artículos como "Aprenda X en días Y", muchos de los cuales son muy buenos. En muchos casos, hay ejemplos de capacitación interactivos: un recorrido por GoLang y GoLang por ejemplo (para GoLang), ejercicios de línea de comandos de NodeSchool (para Javascript, a saber, NodeJS), ejercicios de Scala (para Scala), Python Koans (para Python), etc. .p.

Comenzar con algo complicado no vale la pena. Crear pequeñas aplicaciones y scripts es lo que necesita un principiante. El número total de líneas de código en tales experimentos no supera los 300-400. Lo principal que es necesario en esta etapa es obtener información básica, aprender a programar a cualquier velocidad normal y lo más importante es comprender lo que está haciendo.

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

Un ejemplo de un script inicial que le da una idea a un programador novato sobre cómo funciona su código

La segunda etapa es un estudio más profundo del lenguaje, la creación de un proyecto completo, que ya no se puede llamar "infantil". En muchos casos, debe familiarizarse con la documentación oficial. Para Javascript, se trata de Mozilla Developer Docs , para Swift, Swift Official Docs , para Java, Java Learning Trails , para Python, Python Official Docs t. Se debe prestar especial atención a los cursos en línea con buenos profesores.

También vale la pena explorar otros proyectos de código abierto. Los recursos como la fuente jQuery anotada o la fuente BackboneJS anotada dan una idea de cómo se utiliza un lenguaje de programación particular y bibliotecas adicionales en proyectos profesionales.

Todo esto ayudará a crear su propio proyecto serio, por ejemplo, una aplicación de escritorio, una aplicación web, un programa móvil. Intente utilizar bibliotecas externas cuando necesite herramientas y funciones adicionales.



No se olvide del rendimiento de su aplicación, siempre trate de obtener información de las últimas fuentes. Las oportunidades de aprendizaje son infinitas, puedes mejorar para siempre. Pero al final, puede sentir que se ha convertido en un profesional desde un principiante, y simplemente no hay mejor sensación en el mundo.
Skillbox recomienda:

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


All Articles