S>Обрати внимание, что у нас есть некая переменная power5, состояние которой последовательно меняется. Результат последнего оператора напрямую, но неявно, зависит от того, что положил в эту переменную первый оператор. Это и есть глобальное, с точки зрения отдельных операторов, состояние. S>Вся локальность power5 сводится к тому, что мы запрещаем вмешиваться в ее состояние другим процедурам. S>Стоит также отметить, что уже здесь мы пользуемся процедурой умножения, которая тоже может быть записана как определенная последовательность ветвлений и присваиваний. То, что в реальных архитектурах такая операция доступна "из коробки", для теории значения не имеет.
Глобальное, с точки зрения отдельных операторов, состояние — это весьма странный зверь.
Под глобальными переменными, обычно, понимают переменные доступные в любой области видимости.
Конструкция, описанная тобой какая-то совершенно искуственная.
Какая от неё польза?
EC>>Мне вот неочевидно почему в нём обязательно должно быть глобальное состояние. S>А куда же оно денется, это состояние? Вот попробуй переписать Power5 так, чтобы в ней не было состояния.
Глобального состояния (в традиционном его понимании) там нет.
Здравствуйте, EvilChild, Вы писали:
EC>Конструкция, описанная тобой какая-то совершенно искуственная. EC>Какая от неё польза?
Не понимаю вопроса. Польза от чего? От этой убогой программы? Ну, она умеет возводить числа в пятую степень.
S>>А куда же оно денется, это состояние? Вот попробуй переписать Power5 так, чтобы в ней не было состояния. EC>Глобального состояния (в традиционном его понимании) там нет.
Достаточно того, что есть состояние, изменяющееся во времени. Даже если написать эту же программу в виде
return x*x*x*x*x;
Всё равно будет состояние, хоть и неявное. Потому, что спецификация императивного ЯП подразумевает вот такую семантику:
И никуда ты от этого не денешься, пока не начнешь искать не способ представить алгоритм в виде последовательных трансформаций некоего состояния, а как способ описания зависимостей.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
EC>>Конструкция, описанная тобой какая-то совершенно искуственная. EC>>Какая от неё польза? S>Не понимаю вопроса. Польза от чего? От этой убогой программы? Ну, она умеет возводить числа в пятую степень.
Не прикидывайся, ты даже верно процитировал.
Имелось в виду твоё изобретение под названием глобальное, с точки зрения отдельных операторов, состояние.
S>Достаточно того, что есть состояние, изменяющееся во времени. Даже если написать эту же программу в виде S>
S>return x*x*x*x*x;
S>
S>Всё равно будет состояние, хоть и неявное. Потому, что спецификация императивного ЯП подразумевает вот такую семантику: S>
S>И никуда ты от этого не денешься, пока не начнешь искать не способ представить алгоритм в виде последовательных трансформаций некоего состояния, а как способ описания зависимостей.
Совсем недавно ты настаивал на глобальности состояния, это меня и удивило.
Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, EvilChild, Вы писали:
EC>>Конструкция, описанная тобой какая-то совершенно искуственная. EC>>Какая от неё польза? S>Не понимаю вопроса. Польза от чего? От этой убогой программы? Ну, она умеет возводить числа в пятую степень.
S>>>А куда же оно денется, это состояние? Вот попробуй переписать Power5 так, чтобы в ней не было состояния. EC>>Глобального состояния (в традиционном его понимании) там нет.
S>Достаточно того, что есть состояние, изменяющееся во времени. Даже если написать эту же программу в виде S>
S>return x*x*x*x*x;
S>
S>Всё равно будет состояние, хоть и неявное. Потому, что спецификация императивного ЯП подразумевает вот такую семантику: S>
S>И никуда ты от этого не денешься, пока не начнешь искать не способ представить алгоритм в виде последовательных трансформаций некоего состояния, а как способ описания зависимостей.
Подожди-подожди... Почему это вдруг эти переменные стали состоянием, да ещё и глобальным? Давай напишем так:
[c#]
return x * (\x -> x * x (x)); // Упростили к кубу
[c#]
Умножаем х на результат вычисления лямбды. Где состояние? В регистрах? А они здесь при чём? Это детали реализации.
<< RSDN@Home 1.2.0 alpha 4 rev. 1128>>
Сейчас играет Оргия праведников — С.М.С.
Здравствуйте, thesz, Вы писали:
T>"Не надо путать причину и следствие. Особенно не надо путать следствие." (C) Кнышев
T>Це ж описание монад. Как следствие, не надо его путать с ленивыми вычислениями.
Здравствуйте, Gajdalager, Вы писали: G>Подожди-подожди... Почему это вдруг эти переменные стали состоянием, да ещё и глобальным?
Переменные == состояние. По определению. G>Давай напишем так: G>
G>return x * (\x -> x * x (x)); // Упростили к кубу
G>[c#]
Не понял, что это за синтаксис. Можно пояснить, где здесь что? Имеется в виду вот это:
[c#]
return x * ((int a) => a*a) (x);
Так что ли? G>Умножаем х на результат вычисления лямбды. Где состояние? В регистрах? А они здесь при чём? Это детали реализации.
Ну давайте разберемся. Семантика C# такова, что мы получим примерно вот это:
var square = Func<int, int>(a => a*a);
var temp = square(x);
var temp2 = temp*x;
return x;
Введением императивной лямбды ты ничего не добьешься — это по-прежнему тот же самый пример с x*x*x*x. Такая лямбда — всего лишь еще один, более компактный способ описания процедур. Ровно с тем же успехом ты мог ее описать заранее, как Power2(x) и вызвать по месту. По-прежнему связь результата power5 и аргумента выражена неявно, через цепочку трансформаций.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>Введением императивной лямбды ты ничего не добьешься — это по-прежнему тот же самый пример с x*x*x*x. Такая лямбда — всего лишь еще один, более компактный способ описания процедур. Ровно с тем же успехом ты мог ее описать заранее, как Power2(x) и вызвать по месту. По-прежнему связь результата power5 и аргумента выражена неявно, через цепочку трансформаций.
По моему ты зря нажимаешь на реализацию. Тот код на Ocaml который я выше привел тоже транслируется в очень даже императивное:
_camlKjljl__aux_60:
L101:
cmp ebx, 1
jne L100
ret
L100:
add ebx, -2
mov edx, DWORD PTR [ecx+12]
sar edx, 1
dec eax
imul eax, edx
inc eax
jmp L101
Здравствуйте, FR, Вы писали:
FR>Здравствуйте, Sinclair, Вы писали:
S>>Введением императивной лямбды ты ничего не добьешься — это по-прежнему тот же самый пример с x*x*x*x. Такая лямбда — всего лишь еще один, более компактный способ описания процедур. Ровно с тем же успехом ты мог ее описать заранее, как Power2(x) и вызвать по месту. По-прежнему связь результата power5 и аргумента выражена неявно, через цепочку трансформаций.
FR>По моему ты зря нажимаешь на реализацию.
Я нажимаю не на реализацию, а на семантику.
Я не виноват, что в императивном языке она именно такая. Есть понятия ассоциативности операций и порядка вычисления аргументов.
Это в таком простом случае программа выглядит похожей на функциональную.
Тот код на Ocaml который я выше привел тоже транслируется в очень даже императивное:
Совершенно неважно, во что именно он транслируется. Понятно, что внутре у ней неонка... тьфу, то есть императивный процессор, у которого неизбежно есть состояние.
Но завтра ты скачаешь новый компилятор, и код будет транслироваться во что-то другое. Речь именно о семантике программы.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
FR>>По моему ты зря нажимаешь на реализацию. S>Я нажимаю не на реализацию, а на семантику. S>Я не виноват, что в императивном языке она именно такая. Есть понятия ассоциативности операций и порядка вычисления аргументов. S>Это в таком простом случае программа выглядит похожей на функциональную.
Вот этот код на D
int power5(int x) pure
{
return x * x * x * x *x;
}
Уже непросто выглядит функциональным, но и семантически чистый.
В общем по моему грань слишком тонкая.
Здравствуйте, FR, Вы писали:
FR>Уже непросто выглядит функциональным, но и семантически чистый. FR>В общем по моему грань слишком тонкая.
Между чем и чем? Между декларативным и императивным? Не вижу никакой тонкости грани.
То, что существует полная эквивалентность туда и обратно — ну так это опять же еще Черч показал.
То, что можно внедрять в императивный в целом язык декларативные элементы — тоже нифига не новость.
Вот я на SQL пишу:
update employee set salary = salary +200 where lastname like 'D%'
update employee set salary = salary*1.2 where firstname like 'J%'
Каждый из стейтментов по существу вполне декларативен; он не навязывает никакого внутреннего порядка выполнения. Но они меняют глобальное состояние, и программа остается целиком императивной.
Поэтому мы возвращаемся к вопросу мышления — императивному программисту претит вот эта декларативная неопределенность; он инстинктивно стремится к переменным и циклам.
А декларативному программисту напротив, не нравится вот эта вот зависимость результата от порядка выполнения.
Ему комфортнее работать с
create view Salary2009 as select id, salary+200 as salary from employee where lastname like 'D%'
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>Ну, то есть если мы откажемся от концепции return как таковой
От концепции return не отказываются даже в функциональных языках, там просто отказываются от ключевого слова return. В том же примере FR:
let rec power5 x =
let rec aux acc n = if n=0 thenaccelseaux (acc * x) (n - 1)in aux 1 5;;
уши этой концепции торчат наружу.
S>Никаких присваиваний нет; никакого состояния тоже нет — программа описывает только непреложные факты.
Состояние просто перенесно из изменяемых переменных на стек, но оно все равно в явном виде присутствует -- при организации рекурсивного вызова sum_impl программист должен явно его определять (вычисляя аргументы рекурсивного вызова). Более того, программист должен специальным образом заботится об этом состоянии -- чтобы рекурсия была хвостовой.
S>Вроде того, что сумма всех чисел в массиве — это сумма одного числа с суммой всех остальных. Этот факт истинен всё время, а не только в момент выхода из подпрограммы.
Ерунда. На любом произвольном шаге исполнения функциональной версии sum (с рекурсивными вызовами sum_impl) есть только текущее значение, которое станет результатом (т.е. истинным фактом) только после завершения всего вычисления.
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, eao197, Вы писали:
E>От концепции return не отказываются даже в функциональных языках, там просто отказываются от ключевого слова return. В том же примере FR: E>
E>let rec power5 x =
E> let rec aux acc n = if n=0 thenaccelseaux (acc * x) (n - 1)in aux 1 5;;
E>
E>уши этой концепции торчат наружу.
Не вижу никаких ушей. Это не return, это тождество. Вот в учебнике математики за пятый класс, когда мы говорим "пусть x равно y+z", мы что, что-то куда-то вернули?
E>Состояние просто перенесно из изменяемых переменных на стек, но оно все равно в явном виде присутствует -- при организации рекурсивного вызова sum_impl программист должен явно его определять (вычисляя аргументы рекурсивного вызова).
Не вижу никаких вычислений. Вижу определение функции aux, которая совершенно неизменна, хоть и ссылается сама на себя.
E>Более того, программист должен специальным образом заботится об этом состоянии -- чтобы рекурсия была хвостовой.
Это артефакты конкретных реализаций. С точки зрения частично вычислимых функций, совершенно неважно, является ли рекурсия хвостовой.
E>Ерунда. На любом произвольном шаге исполнения функциональной версии sum (с рекурсивными вызовами sum_impl) есть только текущее значение, которое станет результатом (т.е. истинным фактом) только после завершения всего вычисления.
О каком еще "шаге" мы говорим? С учетом того, что у функций нет побочных эффектов, компилятор имеет полное право вообще отказаться от шагов вычисления и брать результат напрямую из таблицы. "Шаг" может появиться только после преобразования декларативной программы в императивную. То, что тебе очевиден ровно один такой способ преобразования, не означает, что применяться будет именно он.
В частности, хитрый компилятор может раскрыть скобки и получить, что power 5 =1 * x * x * x * x * x, воспользоваться ассоциативностью умножения и получить эквивалент
let rec power5 x =
let rec aux a = a * a in x * aux aux x
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
E>>От концепции return не отказываются даже в функциональных языках, там просто отказываются от ключевого слова return. В том же примере FR: E>>
E>>let rec power5 x =
E>> let rec aux acc n = if n=0 thenaccelseaux (acc * x) (n - 1)in aux 1 5;;
E>>
E>>уши этой концепции торчат наружу. S>Не вижу никаких ушей. Это не return, это тождество.
Ага, тождество.
В OCaml, на котором FR привел пример, значением функции будет значение последнего выражения, которое было выполнено в этой функции. И тип этого выражения должен совпадать с типом функции. А это означает, что в обоих ветках if-а нужно заботиться о выражениях, которые должны иметь одинаковый тип. Поэтому в OCaml запись:
if n=0 thenacc ...
полностью соответствует аналогу в императивных языках:
if(n=0) return acc; ...
S>Вот в учебнике математики за пятый класс, когда мы говорим "пусть x равно y+z", мы что, что-то куда-то вернули?
Программирование -- это не математика. Если бы было по другому, то запись 'a=b' разражала бы всех программистов, а не только Вирта с Мейром.
E>>Состояние просто перенесно из изменяемых переменных на стек, но оно все равно в явном виде присутствует -- при организации рекурсивного вызова sum_impl программист должен явно его определять (вычисляя аргументы рекурсивного вызова). S>Не вижу никаких вычислений. Вижу определение функции aux, которая совершенно неизменна, хоть и ссылается сама на себя.
aux (acc * x)(n - 1)
Посмотри внимательнее. И стоит только переписать вычисление вот так:
acc * (aux x (n-1))
как все очень и очень серьезно меняется. Хотя смысл остается тем же самым.
А на счет изменности функций:
int sum( int[] a ) {
int r = 0;
for( int i = 0; i != a.length; ++i )
r += a[ i ];
return r;
}
Чем эта функция изменна?
E>>Более того, программист должен специальным образом заботится об этом состоянии -- чтобы рекурсия была хвостовой. S>Это артефакты конкретных реализаций.
Еще раз, программирование -- это не математика. Нет никакого толка от функции sum, если она не может вычислить сумму элементов массива из 10M элементов из-за нехватки стека.
Еще одно серьезное различие между программированием и математикой в том, что числа в программировании имеют размерность. В математике выражение (a-b) имеет смысл всегда. А в программировании -- нет.
E>>Ерунда. На любом произвольном шаге исполнения функциональной версии sum (с рекурсивными вызовами sum_impl) есть только текущее значение, которое станет результатом (т.е. истинным фактом) только после завершения всего вычисления. S>О каком еще "шаге" мы говорим?
Например, об очередном вызове sum_impl.
S>С учетом того, что у функций нет побочных эффектов
Какие побочные эффекты у приведенной выше императивной функции sum?
S>компилятор имеет полное право вообще отказаться от шагов вычисления и брать результат напрямую из таблицы.
Из какой таблицы? Из какой таблицы компилятор может подставить значение функции sum для произвольного массива?
S>"Шаг" может появиться только после преобразования декларативной программы в императивную.
Ну да, конечно. Шаг рекурсии в ФП -- это, определенно, совсем другое дело, чем шаг цикла в ИП.
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, Sinclair, Вы писали:
ГВ>>Честно говоря, после "стиль мышления" я выпал в настолько плотный осадок, что остальное уже было несущественным. S>Наличие осадка вижу. Причин считать остальное несущественным — не вижу.
Да как тебе сказать, ИМХО, этот самый "стиль мышления" — причина настолько могучая, что встретив такое в околотехническом тексте остальное можно смело не читать.
ГВ>>То есть "плавный" переход от субъекта к объекту остался незамеченным. То есть ты называешь способ декомпозиции стилем мышления. S>Хорошо. Речь идет о стиле мышления, для которого типичен именно указанный способ декомпозиции.
Ну понятно... Прикладная дистанционная психология рулит. Ещё одна иллюстрация к тому, что если у двух явлений нет явной причинно-следственной связи, то можно совершенно безнаказанно прилепить в качестве этой связи всё, что заблагорассудится. В простейшем случае можно просто объявить явления связанными — и всё.
S>Суть беседы пока что сводится к тому, что выражение "У Мэри есть ягненок" тебя не устраивает, а самостоятельно выполнить переход к "гражданка Мария Джонс осуществляет ограниченное право владения объектом животноводства, не достигшим репродуктивного возраста" ты не хочешь.
Я против того, чтобы узнав о том, что у Мэри есть ягнёнок, в её адрес фыркали: "Глупая пастушка, фи!" А у Влада, в отличие от других авторов аналогичных пассажей, именно так и получается. Ты льёшь воду на ту же мельницу.
ГВ>>Не дела ради, а курьёзу для, попробуем обсудить означенный тобой способ декомпозиции и возможность его существования. S>Отлично. Достаточно подробно вопрос существования такого способа декомпозиции программ освещен в работах Тьюринга, Поста, Чёрча, и многих других.
Я имел в виду практическое существование, то есть факты того, что такой способ используется людьми в повседневной деятельности для разработки ПО. Понятно, что память, например, она вообще глобальна и едина для всех программ.
ГВ>>И здесь вопросов снова больше, чем ответов. Например, так ли уж любой алгоритм подвергается привязке только к глобальным данным? S>Данные, которыми оперирует машина Тьюринга, вполне себе глобальны. Есть какие-то вопросы о возможности реализации любого алгоритма машиной Тьюринга?
Я не о теоретической возможности, а о практике. На практике императивный подход в абсолютном большинстве случаев комбинируется с другими способами декомпозиции, в т.ч. — и с функциональной декомпозицией. Ни для кого это, по большому счёту не секрет, не новость и не открытие нового мира. А выбор способа декомпозиции диктуется совсем не одним только "стилем мышления".
ГВ>>Кроме того, если набор императивных конструкций ограничен модификацией, переходом и ветвлением (условным переходом), то по логике вещей, и вся программа должна представлять собой сплошной спагетти из if/goto. S>Совершенно верно. См. например алгоритм Евклида. Есть какие-то сомнения в том, что он представляет собой сплошное спагетти из if/goto и присваиваний?
Нет сомнний, но это же и есть та самая короткая программа, о которой я сказал.
ГВ>>Если это так, то напрашивается мысль о том, что "императивный программист" — это либо сферический конь в вакууме, S>Ага, похоже начинаются проблески понимания.
ГВ>>либо, в крайнем случае, очень-очень начинающий, поскольку на практике такие программы хоть и бывают, но они либо весьма ограничены в размерах, либо встречаются крайне редко. S>Совершенно верно. Расширим модель "императивного программиста" до "процедурного программиста".
Зачем? Речь шла об императивном программисте.
S>У него в арсенале появляются новые конструкции, в частности процедуры. S>Это даёт ему более богатые возможности декомпозиции алгоритма, но принципиально ничего не меняется — по-прежнему есть разделяемое состояние, с которым идет работа. По-прежнему основным инструментом является if, иногда замаскированный под while/for/until. Даже итерация по массиву/списку выполняется при помощи явной модификации переменной.
Сиречь, имеем комбинацию императивного и какого-то другого подхода.
ГВ>>Оба предполагаемых явления не слишком достойны того, чтобы с ними бороться. Либо я чего-то в твоих рассуждениях не понимаю. S>Не вижу ничего особенно редкого в императивном программировании. Вон по соседству Павел Дворкин постоянно съезжает на то, что декларативное программирование мешает ему точно указать, в каком порядке в какие регистры складывать данные.
Я тоже не вижу ничего редкого в императивном программировании самом по себе (то есть в использовании приёмов, классифицируемых как ИП). Если ты не понял, то я говорил о "чистом императивном программировании", которым, якобы, занимается пресловутый "императивный программист".
ГВ>>P.S.: Вопрос о мышлении по-прежнему остаётся открытым. S>Многие вещи непонятны нам не оттого, что понятия наши слабы, а оттого, что они не входят в круг наших понятий. Это не о тебе, а об "императивных программистах". Влад пытается расширить круг понятий у тех людей, у которых он узкий. Не вижу причин считать это личным оскорблением.
Мда. Чудесатее и чудесатее. Антон, ты хоть понимаешь, о чём я толкую?
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Здравствуйте, eao197, Вы писали:
E>Программирование -- это не математика. Если бы было по другому, то запись 'a=b' разражала бы всех программистов, а не только Вирта с Мейром.
Есть разные виды программирования.
E>как все очень и очень серьезно меняется. Хотя смысл остается тем же самым.
Это артефакт поведения конкретного компилятора конкретного языка.
E>А на счет изменности функций: E>
E>int sum( int[] a ) {
E> int r = 0;
E> for( int i = 0; i != a.length; ++i )
E> r += a[ i ];
E> return r;
E>}
E>
E>Чем эта функция изменна?
Не понял вопрос. Вам непонятно, что r является изменчивым состоянием? что i является изменчивым состоянием, и даже банальное доказательство того факта, что ни одно из обращений не выходит за пределы границ массива является нетривиальным?
E>Еще раз, программирование -- это не математика. Нет никакого толка от функции sum, если она не может вычислить сумму элементов массива из 10M элементов из-за нехватки стека.
Еще раз: для чистой функции никаких причин жрать стек такими объемами нету вообще. Если компилятор неспособен свести ее потребление памяти к двум регистрам — это проблемы компилятора, а не ФП.
E>Еще одно серьезное различие между программированием и математикой в том, что числа в программировании имеют размерность.
Вот этого не понял. E>В математике выражение (a-b) имеет смысл всегда. А в программировании -- нет.
Смысл этого выражения существенно зависит от того, о какой именно математике мы говорим. Если a и b являются членами конечной группы размером 2^32, то в программировании и в математике выражение будет иметь совершенно одинаковый смысл.
E>Например, об очередном вызове sum_impl.
Вы путаете два различных мира. Концепция "вызова" — это процедурное программирование. От императивного программирования в нём есть понятие "instruction pointer" — инструкции, выполняемой в данный момент. Плюс паттерн "вызов с возвратом", который строится из запоминания текущего ip, последующего goto, и восстановления ip+1 в будущем.
E>Какие побочные эффекты у приведенной выше императивной функции sum?
У приведенной выше — никаких. Поэтому хороший компилятор избавляется от излишней императивности в меру своих возможностей. E>Из какой таблицы? Из какой таблицы компилятор может подставить значение функции sum для произвольного массива?
В функции power5 никакого массива не было.
E>Ну да, конечно. Шаг рекурсии в ФП -- это, определенно, совсем другое дело, чем шаг цикла в ИП.
Воот. Начинаешь понимать.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, eao197, Вы писали:
E>Еще одно серьезное различие между программированием и математикой в том, что числа в программировании имеют размерность. В математике выражение (a-b) имеет смысл всегда. А в программировании -- нет.
a и b — логические предикаты (или ещё лучше прямые на плоскости), объясни математический смысл выражения a-b
Здравствуйте, Sinclair, Вы писали:
E>>А на счет изменности функций: E>>
E>>int sum( int[] a ) {
E>> int r = 0;
E>> for( int i = 0; i != a.length; ++i )
E>> r += a[ i ];
E>> return r;
E>>}
E>>
E>>Чем эта функция изменна? S>Не понял вопрос. Вам непонятно, что r является изменчивым состоянием? что i является изменчивым состоянием, и даже банальное доказательство того факта, что ни одно из обращений не выходит за пределы границ массива является нетривиальным?
Не знаю, что такое изменная-неизменная функция; однако думаю, что данную функцию можно назвать чистой, ведь она возвращает одно и то же значение для того самого аргумента и не имеет побочных эффектов. Если эффекты есть — укажите мне их. А то, что функция записана в императивном стиле — это уже детали.
<< RSDN@Home 1.2.0 alpha 4 rev. 1128>>
Сейчас играет Оргия праведников — Сицилийский виноград