Помощь новичкам

Эээ, почему не эффективно? Для чего тогда придумывалась вся эта функциональщина в паскале, если все оно не эффективно? Опять же если не эффективно, то в какой части и по сравнению с чем? Если сравнить тот же перебор в цикле по всем K от 1 до какого-то числа, это будет намного эффективней? Ну да, маленький мальчик Гаусс решил задачку эффективно, но это не везде такое возможно. Да и что же, над каждой задачей сидеть и ломать голову, как можно ее решить наиболее эффективно? Может мне не важна эффективность в данном случае. Вот мне бы, в первую очередь, хотелось бы поменьше кода и побольше ясности. А потом уже можно и об эффективности подумать.

А в чем преимущество использования First перед Take?

Тренировки все же лучше устраивать там, где получаешь какое-то преимущество. То, что функциональное программирование (в основе которого лежит рекурсия) позволяет реализовать итерационный алгоритм, построенный на базе цикла, известно и доказано (рекурсия и итерация взаимозаменяемы, хоть и с разной степенью эффективности). Но функциональное программирование ради функционального программирования - это не просто глупость, это - зло. Функциональное программирование хорошо там, где запись алгоритма выглядит проще и понятнее. Для данной задачи понятнее цикл, даже если не говорить о какой-то там эффективности. Поэтому хорош тот язык, где можно функциональную парадигму использовать там, где ей место, а не везде.

.First - это .Take(1), .Last - это .TakeLast(1) Никаких преимуществ, кроме более короткой записи.

По скорости последовательности всегда хуже обычных циклов. Хотя бы потому что делается полно вызовов подпрограмм. А ещё создаются объекты в куче (последовательности это классы). В то время как в циклах память вообще не выделяется (используется из стека, а там все выделено при старте программы).

Придумывалось оно чтоб сократить код и сделать его более понятным.

Вот только в данном случае вы ни того ни того не получите через последовательности. Вот, к примеру, математическое решение:

begin
  var res := sqrt(1+8*ReadlnInteger('N ='))/2 - 0.5;
  Ceil(res).Print;
end.

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

Бред.

В нулевых

У них возвращаемое значение разное, .Take не заменит .First если вам надо вызвать для результата что то кроме .Print.

Во первых, .Take-и создают ещё 1 класс-последовательности, который ещё раз нужно перебирать. Лишние затраты на вызовы.

Во вторых MoveNext (код выбирающий перебирать ли элементы дальше) - более сложный и затратный.

В третьих - я сейчас посмотрел в .TakeLast, и это отборные костыли, как хуже реализовать - и придумать сложно. (#1678)

1 лайк

Ваше “костыли” настолько примелькалось. что я уже не обращаю внимания на это слово. Это как когда какой-то человек, к примеру, постоянно матерится, на мат через какое-то время внимание просто перестаешь обращать, понимая, что это у него идет от невоспитанности. Как Ваши посты почитать - ну весь PascalABC.NЕТ - один большой костыль. Ну напишите уж лучше, что ли, раз Вы настолько большой специалист.

Где-то заменит, где-то - нет. Надо смотреть по алгоритму решения, что именно требуется.

Скажем, повторное вычисление сумм, которое спрятано в Select – неэффективно. “Функциональщина”, как Вы выражаетесь, придумана для тех, кто хочет написать короткий и понятный код, не слишком заботясь о наивысшей скорости выполнения программы. Вы сами должны выбирать инструмент под свои задачи, никто это за Вас не сделает. Вас же не смущает, что есть и ложка, и вилка, и что вилкой суп есть не очень удобно :slight_smile: И это не повод отказываться от вилки.

Это собирать грибы лучше там, где они растут. А искать можно везде, главное, чтобы пейзаж нравился :slight_smile: Если же серьёзно, не вижу причин, почему бы не поупражняться и без получения преимущества. При обучении все бонусы – это новые нейронные связи :wink: Повторюсь: если сам понимаешь, где достоинства, а где недостатки решения.

1 лайк

В справке по Result написано следующее:

“Если внутри функции не присвоить переменной Result некоторое значение, то функция вернет в результате своего вызова непредсказуемое значение.”

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

function foo(): boolean;
begin

end;

begin
    Loop 1000 do Print(foo())
end.

1000 раз возвращает False (или 0, или пустую строку, или nil, при других возвращаемых типах). И ни разу, в этом случае, не возвращает True. Так нужно ли инициализировать Result?

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

И локальные тоже?

Я бы хотел уточнить: в PascalABC.NET - обнуляются, в C# том же - нет.

procedure P();
begin
  var x: byte;
  Writeln(x); // 0
end;

begin
  P();
end.

А почему тут адрес x один и тот же?

procedure P();
begin
  var x: byte;
  Writeln(x, ' ', @x); // ?
end;

begin
  loop 10 do
  begin
    P();
    Sleep(1000);
  end
end.

Да

В IL они обнуляются, поэтому и в C# тоже. Но там ещё есть ненужная защита от дурака, если её отключить - вы увидите что там тоже обнуляется всё.

1 лайк

Потому что x объявляется на стеке. Как стек работает вы знаете?

что за защита от дурака? лишняя инициализация переменной нулем в коде, как наследие крестов?

Вау, теперь я знаю, что такое крестовые походы! :rofl:

Немножко. Вот тут все понятно, почему разные адреса:

procedure P(n: integer);
begin
  if n > 10 then exit; 
  var x: byte;
  Writeln(x, ' ', @x);
  P(n + 1)
end;

begin
  P(1)
end.

Потому что на стеке 10 подпрограмм со своими переменными x. А в том случае в цикле, на любой итерации, на стеке лежит только одна P(). И не понятно, почему переменной x каждый раз дается один и тот же адрес. Других адресов в памяти нет, что ли?

Стек это не куча, на нём всё лежит в определённом порядке, а точнее в том - в котором были вызваны методы (сначала старые, потом новые).

У вас в самом начале идёт ваша Main (begin-end.). Потом P. Когда P завершается - указатель конца стека просто сдвигает назад к Main. Затем снова вызывается P, и её кладёт снова прямо после Main.

А все локальные переменные каждого метода всегда выделяются в момент вызова, в порядке их описания в IL, которое решает компилятор паскаля и только 1 раз, перед созданием .exe .

2 лайка

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

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

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

Нет, они все в 1 стеке выделяются. Хоть типы и разные - это им не мешает.