LINUX.ORG.RU

ЛОР, помоги выбрать ЯП для обучения

 , , , ,


1

3

Часто слышу просьбу научить программированию. Хотя пока я этим не занимаюсь, мне стало интересно, какой ЯП выбрать для введения в программирование.

Вот к каким мыслям я пришёл:

Язык должен наиболее чисто демонстрировать самые основы написания кода.

Не Си и не современные коммерческие языки (Java, C#, Go). Си, хотя примитивный в основе, усложнён из-за окружения, в котором используется. Современные коммерческие языки были созданы для решения проблем индустрии. Проблема общая: я хочу преподавать материал по мере нарастания сложности. Если в языке неизбежно приходится использовать классы или printf, то это затруднит объяснение (не хотелось бы слишком часто говорить «потом узнаешь для чего это нужно»), напугает студента (ему придётся писать код, используя возможности, которые он плохо понимает), создаст неправильное восприятие основ (как будто printf — это какая-то важная часть компьютера или ОС).

В целом, я хочу постепенно наращивать сложность и, если задачу можно решить более простым методом, то выбрать этот метод.

Языки, между которыми я выбираю: Pascal и Python.

Pascal устарел и денег не принесёт (обидно), но это и не является основной целью. Целью является программирование, а не современное окружение.

В частности, я не собираюсь задрачивать студента на Delphi или любой «продвинутый» диалект языка. Это противоречит цели. Я рассчитываю на то, что после должной тренировки “bare bones” нужно перейти на современный язык и это будет легко.

Важно упомянуть, что спека языка Oberon (Виртовский язык, тот же Паскаль, только упрощённый и доработанный) составляет 17 страниц.

Питон мне сложнее оценить, потому что я избегал работы с ним.

Если ограничиться императивным подмножеством, без ассоциативных массивов, классов и мета-классов, list comprehensions, HOF, исключений, то выглядит как альтернатива Паскалю. Хотя меня беспокоит динамическая типизация. Типы — очень важная вещь, хотелось бы чтобы язык помог это донести, а не быть типа «ну да, это важно, но ты забей».

Это все мои мысли.

Что касается практики, то я имел несчастье наблюдать как человек впервые знакомился с программированием, изучая Java на javarush. На это было больно смотреть.

Edit: дальнейшие пояснения по теме:

  • Подробнее про то, почему я считаю, что изучение основ и Паскаль хорошо сочетаются: 1
  • Почему не Си и не ассемблер: 1 2
  • Почему Паскаль: 1 2
  • Почему не Питон: 1
  • Целевая аудитория: 1
  • Почему такая размытая аудитория: 1 2
  • Про важность иерархии: 1


Последнее исправление: kaldeon (всего исправлений: 10)
Ответ на: комментарий от monk

Как вообще в JS или Python тело вызываемой функции может быть недоступно, если она не часть языка?

тело находится во внешнем модуле. для выхова функции не нужно знать код ее тела. а только адрес входа, или индекс какого-нить дескриптора ее описывающего во всяких там интерпретируемых языках.

alysnix ★★★
()
Ответ на: комментарий от alysnix

в динамических языках это регулярные функции, спрятанные в модулях, и от них известны только заголовки.

Уверен? Что в питоне, что в JS требуется весь модуль для компиляции программы, использующей этот модуль.

опять же для всего этого нужен этап компиляции

И в каком динамическом языке это есть? И вообще хотя бы получить описание функций без тела в отдельном файле.

monk ★★★★★
()
Ответ на: комментарий от alysnix

тело находится во внешнем модуле.

А как его туда поместить? И как оттуда загрузить? import требует путь к исходнику.

monk ★★★★★
()
Ответ на: комментарий от monk

А как его туда поместить? И как оттуда загрузить? import требует путь к исходнику.

ты лезешь в детали уже. там много вариантов реализации и читать лекции мне неохота.

если это чистый интерпретатор, то ему надо только тексты модулей. он их будет читать и исполнять.

если это jit компиляции, он будет компилировать код модулей on-demand например в код виртмашины и исполнять на вирт машине.

в любом случае получить эффективность уровня языков типа сишечки нереально. просто потому что для этого надо было иметь отдельный этап компиляции и знать все типы. а поскольку этого динамические языки не знают, они всерьез и не могут подобраться к плюсам по эффективности.

alysnix ★★★
()
Ответ на: комментарий от alysnix

Я лезу в детали, потому что ты обсуждаешь какого-то единорога.

Ни в одном, известном мне, динамическом языке нет возможности запустить программу, не имея доступа к телам используемых модулей.

Соответственно, к чему относится «в динамических языках это регулярные функции, спрятанные в модулях, и от них известны только заголовки» я не понимаю. Приведи хоть один пример такого языка.

они всерьез и не могут подобраться к плюсам по эффективности

Плюсы с auto уже де-факто динамический язык.

monk ★★★★★
()
Ответ на: комментарий от monk

сформулируй предмет дискуссии, я уже не понимаю о чем она. если то что статические типы дают возможность изготавливать много более эффективный код чем динамические - это очевидно, примерно как дважды два.

не веришь - godbolt в руки. там и питон есть небось. более эффективного кода чем у плюсов ты там не увидишь. и не потому что плюсы эффективны сами по себе, а потому, что они статически типизированы и используют оптимизации по максимуму, имея полную инфу. но любой статически типизированный язык можно догнать до эффективности уровня плюсов.а вот догнать до плюсов динамически типизировнный немамона! немамона даже приблизиться.

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от alysnix

если то что статические типы дают возможность изготавливать много более эффективный код чем динамические - это очевидно, примерно как дважды два.

Плюсы с auto дают более эффективный код, чем плюсы с указанными вручную типами.

monk ★★★★★
()
Ответ на: комментарий от alysnix

статические типы дают возможность изготавливать много более эффективный код чем динамические

Динамический PHP https://benchmarksgame-team.pages.debian.net/benchmarksgame/program/regexredux-php-1.html быстрее статического Паскаля https://benchmarksgame-team.pages.debian.net/benchmarksgame/program/regexredux-fpascal-1.html

а потому, что они статически типизированы и используют оптимизации по максимуму

Достаточно второй части. Нынешний SBCL даёт более быстрый код, чем gcc 2.95.

monk ★★★★★
()
Ответ на: комментарий от alysnix

но любой статически типизированный язык можно догнать до эффективности уровня плюсов

Не любой, а только с UB. Даже safe Rust уже нельзя.

monk ★★★★★
()
Ответ на: комментарий от alysnix

более эффективного кода чем у плюсов ты там не увидишь

Что за пропаганда С++? Более неэффективного кода, чем у плюсов, не увидишь. Потому что любители плюсов так обмазываются абстракциями в несколько слоёв, что теряют всякую связь с реальностью.

jpegqs
()
Последнее исправление: jpegqs (всего исправлений: 1)
Ответ на: комментарий от monk

Не любой, а только с UB.

программа с UB - это некорректная программа. вы только что написали следующее - «с++ превосходит всех в эффективности кода генерируемого для некорректных программ».

вообще мало кому, в своем уме, интересна эффективность кода некорректных программ.

alysnix ★★★
()
Ответ на: комментарий от jpegqs

Вы вылезли со своей пропагандой ненужных абстракций

каких «ненужных абстракций» я вылез с пропагандой?

alysnix ★★★
()
Ответ на: комментарий от alysnix

Есть язык Си, а есть С++ который считает себя «улучшенным Си», но это лишь абстракции для фанатов абстракций. На С++ бывают пишут так что это почти что чистый Си, но думают что это С++. А бывают пишут многоярусные темплейты бесконечной вложенности. Где ни о какой производительности не может идти речи, потому что у программиста нет связи с реальностью. Что и есть максимальное использование С++. Но вы утверждаете, что С++ код самый эффективный. Маленькие примеры, где компилятор может всё разинлайнить - ничего не доказывают.

jpegqs
()
Ответ на: комментарий от alysnix

программа с UB - это некорректная программа. вы только что написали следующее - «с++ превосходит всех в эффективности кода генерируемого для некорректных программ».

При чём тут программа с UB? Речь про язык с UB. То есть про язык, который часть синтаксически правильных программ объявляет некорректными. С++ превосходит всех в эффективности кода, потому что часть синтаксически правильных программ считает некорректными и допускает для них любые действия при выполнении.

monk ★★★★★
()
Ответ на: комментарий от jpegqs

На С++ бывают пишут так что это почти что чистый Си, но думают что это С++. А бывают пишут многоярусные темплейты бесконечной вложенности.

вы критикуете предмет из-за его неправильного использования. вы утверждаете, что если кто-то надевает штаны на голову - в этом виноваты штаны.

штаны не виноваты. просто с головой у некоторых непорядок.

аргумент не принимается.

alysnix ★★★
()
Ответ на: комментарий от monk

То есть про язык, который часть синтаксически правильных программ объявляет некорректными.

программа должна быть не только синтаксически правильной, но и семантически, в рамках известных правил.

например, нельзя вызвать функцию которая не обьявлена. это не имеет отношения к синтаксису

alysnix ★★★
()
Ответ на: комментарий от monk

Всё-таки автоматический вывод типов во время компиляции и динамическая типизация - это разные штуки. Например, такой код на Питоне вполне рабочий:

import random

def func():
    if random.random() < 0.5:
        return 1
    else:
        return 'one'


for i in range(5):
    print(func())

Почему он работает? Потому что Питон на самом деле не возвращает число или строку, а объект с информацией о типе.

Kogrom
()
Ответ на: комментарий от alysnix

например, нельзя вызвать функцию которая не обьявлена. это не имеет отношения к синтаксису

Это синтаксис. Его можно проверить не выполняя программу.

А вот нельзя в оператор [] передавать число больше длины массива — это UB. Если такое правило есть, доступ к элементу массива можно сделать быстрее. А если функция должна для любых аргументов или выполнять своё действие или выдавать ошибку, то часть оптимизаций становятся невозможными.

monk ★★★★★
()
Ответ на: комментарий от Kogrom

Здесь тип результата func будет union. И на языке со статической типизацией он будет работать не быстрее.

monk ★★★★★
()
Ответ на: комментарий от monk

Здесь тип результата func будет union.

Прямо-таки через auto выведет union? Можно посмотреть как такой код выглядит?

Kogrom
()
Ответ на: комментарий от Kogrom

Прямо-таки через auto выведет union? Можно посмотреть как такой код выглядит?

Пока не выведет, подсказывать надо:

std::variant<std::string, int> fun() 
{
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_real_distribution<> dis(0, 1.0);  
    if (dis(gen) < 0.5) return 1; else return "aaa";
}

Но в следующей версии C++ может и сам выводить начать (типы ведь уже и так собирает, std::variant уже есть).

monk ★★★★★
()
Ответ на: комментарий от alysnix

синтаксис - синтаксические правила языка, то есть его грамматика.

В случае Си++ наличие функции определяет синтаксис.

int y = f();

Может быть вызовом функции, если есть функция f, а может быть вызовом одноимённого типа, если он есть, может быть вызовом макроса.

monk ★★★★★
()
Ответ на: комментарий от monk

Но в следующей версии C++ может и сам выводить начать

Сильно сомневаюсь, чтобы ради таких редких случаев сильно усложнили анализ кода. Разве что ИИ в компилятор запихают.

В Питоне тоже пока ИИ не сидит, насколько мне известно.

Kogrom
()
Ответ на: комментарий от monk

макросом не может быть, макросы раскрываются препроцессором еще до компиляции.

и какая- разница чем это может быть? если есть формальное грамматическое правило

var = expression…

совершенно неважно чем тут является выражение - оно может быть и сложным выражением и просто вызовом функции или еще чем. для синтаксической корректности главное, чтобы оно удовлетворяло грамматике языка.

alysnix ★★★
()
Ответ на: комментарий от monk

Ещё пример. Допустим у нас такой код:

class A:
    def print(self):
        print('class A')

class B:
    def not_print(self):
        pass

objects = [A(), B()]

for object in objects:
    object.print()

Он выведет ‘class A’, а затем упадёт с ошибкой. Но мы может добавить метод в класс B позже и всё будет хорошо:

class A:
    def print(self):
        print('class A')

class B:
    def not_print(self):
        pass

objects = [A(), B()]

B.print = lambda self: print('class B')

for object in objects:
    object.print()

И наконец, мы может сделать добавление метода в класс B случайным образом, чтобы на стадии компиляции нельзя было знать, будет ли он в классе B или нет.

Kogrom
()
Ответ на: комментарий от Kogrom

Сильно сомневаюсь, чтобы ради таких редких случаев сильно усложнили анализ кода. Разве что ИИ в компилятор запихают.

Здесь анализ кода тривиальный. Также, как если вздумается разрешить повторно объявлять переменную без открывающей фигурной скобки.

monk ★★★★★
()
Ответ на: комментарий от alysnix

макросом не может быть, макросы раскрываются препроцессором еще до компиляции.

Макросы являются частью синтаксиса языка.

совершенно неважно чем тут является выражение - оно может быть и сложным выражением и просто вызовом функции или еще чем. для синтаксической корректности главное, чтобы оно удовлетворяло грамматике языка.

Утверждение «нельзя вызвать функцию которая не обьявлена» относится к синтаксису. Синтаксически нельзя написать вызов необъявленной функции.

monk ★★★★★
()
Ответ на: комментарий от Kogrom

Ещё пример. Допустим у нас такой код:

Можно сделать систему классов на базе GObject или COM, которая будет себя вести также, несмотря на статическую типизацию.

Можно, наоборот, как в CL defstruct запретить менять поля/методы структуры после описания.

monk ★★★★★
()
Ответ на: комментарий от monk

Синтаксически нельзя написать вызов необъявленной функции.

как в грамматике описывается «обьявленная функция»? и чем она отличается необьявленной?

вы путаете синтаксис и семантику. и плохо представляете сам процесс компиляции.

alysnix ★★★
()
Ответ на: комментарий от monk

Ещё можно внедрить библиотеку с поддержкой динамической типизацией. Но при этом автоматический вывод типов не станет динамической типизацией. Это разные механизмы, вот и всё.

Kogrom
()
Ответ на: комментарий от monk

Макросы являются частью синтаксиса языка.

и что? макропроцессор вообще не знает синтаксиса того языка что он процессирует. он знает только синтаксис собственных команд, которые и ищет внутри файла.

у сишного и плюсового препроцессора минимальные различия, связанные только с некоторой разницей в зарезервированных словах обоих языков

alysnix ★★★
()
Ответ на: комментарий от alysnix

как в грамматике описывается «обьявленная функция»? и чем она отличается необьявленной?

Тем, что если символ перед скобкой в выражении является объявленной функцией, то это вызов функции. А необъявленную функцию вызвать нельзя.

monk ★★★★★
()
Последнее исправление: monk (всего исправлений: 1)
Ответ на: комментарий от Kogrom

Но при этом автоматический вывод типов не станет динамической типизацией.

Так во всех современных языках с динамической типизацией именно автоматический вывод типов, аналогичный шаблонам Си++. JIT без этого нормально не работает.

monk ★★★★★
()
Ответ на: комментарий от alysnix

Ладно, пусть будет не слово «синтаксис». Пусть будет «язык, на котором компилятор не может определить, является ли программа корректной, и некорректная программа может делать что угодно».

monk ★★★★★
()
Ответ на: комментарий от monk

Тем, что если символ перед скобкой в выражении является объявленной функцией, то это вызов функции. А необъявленную функцию вызвать нельзя.

просто почитайте что такое синтаксис языка и не говорите ерунду.

https://translated.turbopages.org/proxy_u/en-ru.ru.6a3a2071-67558b29-9b4e80cb-74722d776562/https/en.wikipedia.org/wiki/Syntax_(programming_languages)

там в конце освещается вопрос отношения синтаксиса и семантики.

alysnix ★★★
()
Ответ на: комментарий от alysnix

просто почитайте что такое синтаксис языка и не говорите ерунду.

Вот это что такое в синтаксисе Си++?

auto i = f();

Уверены, что именно вызов необъявленной функции? А почему не вызов необъявленного типа или необъявленного макроса?

monk ★★★★★
()
Ответ на: комментарий от alysnix

там в конце освещается вопрос отношения синтаксиса и семантики.

На нормальных языках семантически некорректная программа должна выдавать ошибку времени выполнения. На Си++ семантически некорректная программа может делать что угодно. Поэтому компилятор Си++ может формировать более быстрый код допуская для семантически некорректных программ порчу данных и прочие непотребства.

monk ★★★★★
()
Ответ на: комментарий от monk

это не вызов, а присваивание. с точки зрения синтаксиса оно верное. с точки зрения семантики - вопрос в том - что такое f. можно ли это вызвать вообще и существует ли для f оператор вызова. может это функтор. а может функция. тип вызвать нельзя. это описание. макрос до компилятора не дойдет, его раскроет препроцессор. то есть вместо f будет еще что-то.

alysnix ★★★
()
Ответ на: комментарий от monk

это вызов не типа, а создание экземпляра конструктором его. конструктор это не тип. а функция, япона мать. просто в с++ имя конструктора и имя типа совпадают.

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от alysnix

конструктор это не тип. а функция

Определяется он типом, а не функцией. И если бы он был функцией, то работало бы

auto x = &f;

Но нет: expected primary-expression before ';' token.

monk ★★★★★
()
Ответ на: комментарий от monk

конструктор это специальная функция. ее можно применять только в правильном контексте, при конструировании обьекта. нет даже такого типа как указатель на конструктор. потому в вашем выражении даже тип левой части вывести невозможно.

alysnix ★★★
()
Ограничение на отправку комментариев: