http://learnyousomeerlang.com/content
Заметки о тестировании, программировании и прочий "поток сознания", который жалко писать "в стол"
суббота, 28 октября 2017 г.
пятница, 27 октября 2017 г.
Вопрос. Haskell
По итогам:
http://programmingmindstream.blogspot.ru/2017/10/haskell-vs-count.html?m=1
А как length/count (количество элементов списка) на Haskell реализовано?
Так:
Count [] = 0
Count ( _ : xs ) = 1 + Count xs
?
Или можно как-то эффективнее?
http://programmingmindstream.blogspot.ru/2017/10/haskell-vs-count.html?m=1
А как length/count (количество элементов списка) на Haskell реализовано?
Так:
Count [] = 0
Count ( _ : xs ) = 1 + Count xs
?
Или можно как-то эффективнее?
четверг, 26 октября 2017 г.
Haskell vs мои скрипты. Count & Map
Integer def List:Count
List in aList
aList Match (
List:[]
0
( List:Make: _ Tail )
( 1 + Tail call.me )
) // aList Match
; // List:Count
// что тут делать с "длинной рекурсией"
// - пока не очень понятно,
// возможно стоит сделать Count
// "хранимым членом"
Тогда так:
Integer def List:Count
List in aList
aList Match (
List:[]
0
( List:Make: _ Tail Count )
(
If ( Count .IsValid ) then
Count
Else
( 1 + Tail call.me )
)
) // aList Match
; // List:Count
List def List:Map
List in aList
Lambda in aLambda
aList Match (
List:[]
List:[]
( List:Make: Head Tail )
List:Make:
^@( Head aLambda do )
^@( Tail aLambda call.me )
) // aList Match
; // List:Map
Ну и вызов:
List:Make 1 List:Make 2 List:Make 3 List:Make 4 List:Make 5 List:[]
// строим список [ 1 2 3 4 5 ]
IsOdd
// отбираем нечётные
List:Map
( ToString + ' ' )
// тут приводим к строке и добавляем пробел
List:Map
List:For:
// перебирается список
Print
// печатаем каждый элемент
Напечатано:
1 3 5
Ну и вызов:
List:Make 1 List:Make 2 List:Make 3 List:Make 4 List:Make 5 List:[]
// строим список [ 1 2 3 4 5 ]
IsOdd
// отбираем нечётные
List:Map
List:Count
Print
// печатаем List:Count
Напечатано:
3
Пока выглядит вроде некузяво.
Но у меня же скрипты изначально императивные. И ОПЗ (обратная польская запись).
Но идею я вроде понял. Там строится граф/дерево разбора выражений в узлах которого записывается либо значение, либо ссылка на функцию (возможно со связанными параметрами, и не факт, что со всеми).
Если это значение, то выражение - вычислено полностью.
Если ссылка на функцию, то вычислено не полностью.
Тут и проявляется "ленивость".
Когда в узле вычисляется функция, то в узел записывается результат вычисления вместо ссылки на функцию.
Тут проявляется кеширование результатов. Ранее вычисленные функции - больше не вычисляются. А представляются значениями записанными в узлах графа/дерева.
Ну и код на Haskell:
xs = 1 : map (*2) xs
Выглядит так:
List def xs
List:Make:
1
( call.me ( 2 * ) List:Map )
; // List:Map
Я честно скажу - не проверял, но должно сработать.
Завтра проверю.
Бесконечной рекурсии там точно не будет.
При вызове xs.
Она сконструирует список:
1 : reference to expression ( xs ( 2 * ) List:Map )
А вот применится ли Map к правильному значению - вопрос пока открыт.
Возможно Map будет применяться не к xs, а к 1 ВСЕГДА.
Тут вопрос пока открыт. Надо проверить.
Возможно надо написать так:
List def xs
List:Make:
1
( Result ( 2 * ) List:Map )
; // List:Map
Есть и такой вариант. И он (как мне кажется) - ближе к истине.
Ибо call.me - это ссылка на функцю, а Result - это уже вычисленное значение.
Тонкая грань.
Или так:
List def xs
List VAR myResult
List:Make:
1
( myResult ( 2 * ) List:Map )
>>> myResult
myResult
; // List:Map)
Хотя и это можно скрыть за "синтаксическим сахаром".
List in aList
aList Match (
List:[]
0
( List:Make: _ Tail )
( 1 + Tail call.me )
) // aList Match
; // List:Count
// что тут делать с "длинной рекурсией"
// - пока не очень понятно,
// возможно стоит сделать Count
// "хранимым членом"
Тогда так:
Integer def List:Count
List in aList
aList Match (
List:[]
0
( List:Make: _ Tail Count )
(
If ( Count .IsValid ) then
Count
Else
( 1 + Tail call.me )
)
) // aList Match
; // List:Count
List def List:Map
List in aList
Lambda in aLambda
aList Match (
List:[]
List:[]
( List:Make: Head Tail )
List:Make:
^@( Head aLambda do )
^@( Tail aLambda call.me )
) // aList Match
; // List:Map
Ну и вызов:
List:Make 1 List:Make 2 List:Make 3 List:Make 4 List:Make 5 List:[]
// строим список [ 1 2 3 4 5 ]
IsOdd
// отбираем нечётные
List:Map
( ToString + ' ' )
// тут приводим к строке и добавляем пробел
List:Map
List:For:
// перебирается список
// печатаем каждый элемент
Напечатано:
1 3 5
Ну и вызов:
List:Make 1 List:Make 2 List:Make 3 List:Make 4 List:Make 5 List:[]
// строим список [ 1 2 3 4 5 ]
IsOdd
// отбираем нечётные
List:Map
List:Count
// печатаем List:Count
Напечатано:
3
Пока выглядит вроде некузяво.
Но у меня же скрипты изначально императивные. И ОПЗ (обратная польская запись).
Но идею я вроде понял. Там строится граф/дерево разбора выражений в узлах которого записывается либо значение, либо ссылка на функцию (возможно со связанными параметрами, и не факт, что со всеми).
Если это значение, то выражение - вычислено полностью.
Если ссылка на функцию, то вычислено не полностью.
Тут и проявляется "ленивость".
Когда в узле вычисляется функция, то в узел записывается результат вычисления вместо ссылки на функцию.
Тут проявляется кеширование результатов. Ранее вычисленные функции - больше не вычисляются. А представляются значениями записанными в узлах графа/дерева.
Ну и код на Haskell:
xs = 1 : map (*2) xs
Выглядит так:
List def xs
List:Make:
1
( call.me ( 2 * ) List:Map )
; // List:Map
Я честно скажу - не проверял, но должно сработать.
Завтра проверю.
Бесконечной рекурсии там точно не будет.
При вызове xs.
Она сконструирует список:
1 : reference to expression ( xs ( 2 * ) List:Map )
А вот применится ли Map к правильному значению - вопрос пока открыт.
Возможно Map будет применяться не к xs, а к 1 ВСЕГДА.
Тут вопрос пока открыт. Надо проверить.
Возможно надо написать так:
List def xs
List:Make:
1
( Result ( 2 * ) List:Map )
; // List:Map
Есть и такой вариант. И он (как мне кажется) - ближе к истине.
Ибо call.me - это ссылка на функцю, а Result - это уже вычисленное значение.
Тонкая грань.
Или так:
List def xs
List VAR myResult
List:Make:
1
( myResult ( 2 * ) List:Map )
>>> myResult
myResult
; // List:Map)
Хотя и это можно скрыть за "синтаксическим сахаром".
среда, 25 октября 2017 г.
ToDo. Сделать глобальную writeonly "переменную" _
Сделать глобальную writeonly "переменную" _
WriteOnly _
Туда можно писать, но нельзя читать.
Это аналог devNull.
Это нужно для pattern matching'а.
Update. Сделал.
WriteOnly _
Туда можно писать, но нельзя читать.
Это аналог devNull.
Это нужно для pattern matching'а.
Update. Сделал.
ToDo. Сделать match вместо RULES
Сделать match:
Чтобы вместо:
VAR head
VAR tail
RULES
( aList Empty == )
Empty
( aList .ConstructedWith: List )
(
aList .getValues ( @ head @ tail )
head : tail
)
DEFAULT
aList
>>> Result
;
Или:
VAR head
VAR tail
RULES
( aList .ConstructedWith: Empty )
Empty
( aList .ConstructedWith: List )
(
aList .getValues ( @ head @ tail )
head : tail
)
DEFAULT
aList
>>> Result
;
Написать:
Match aList
( Empty : Empty )
( matched List ( head tail ) : ( head : tail ) )
( Default : aList )
>>> Result
;
Чтобы можно было pattern matching делать.
И переменные (head и tail в нашем случае) определялись в нужном контексте.
Или даже так:
Match aList
( Empty ? Empty )
( matched List head tail ? ( head : tail ) )
( matched Join head tail ? ( head : tail ) )
( Default ? aList )
>>> Result
;
Или даже так:
Match aList
( Empty ? Empty )
( List head tail ? ( head : tail ) )
( Join head tail ? ( head : tail ) )
( Default ? aList )
>>> Result
;
Чтобы вместо:
VAR head
VAR tail
RULES
( aList Empty == )
Empty
( aList .ConstructedWith: List )
(
aList .getValues ( @ head @ tail )
head : tail
)
DEFAULT
aList
>>> Result
;
Или:
VAR head
VAR tail
RULES
( aList .ConstructedWith: Empty )
Empty
( aList .ConstructedWith: List )
(
aList .getValues ( @ head @ tail )
head : tail
)
DEFAULT
aList
>>> Result
;
Написать:
Match aList
( Empty : Empty )
( matched List ( head tail ) : ( head : tail ) )
( Default : aList )
>>> Result
;
Чтобы можно было pattern matching делать.
И переменные (head и tail в нашем случае) определялись в нужном контексте.
Или даже так:
Match aList
( Empty ? Empty )
( matched List head tail ? ( head : tail ) )
( matched Join head tail ? ( head : tail ) )
( Default ? aList )
>>> Result
;
Или даже так:
Match aList
( Empty ? Empty )
( List head tail ? ( head : tail ) )
( Join head tail ? ( head : tail ) )
( Default ? aList )
>>> Result
;
Haskell. Pattern matching
checkIP :: IPAddress -> String
checkIP (IPv4 address) = "IPv4 is '" ++ address ++ "'."
checkIP IPv4Localhost = "IPv4, localhost."
checkIP (IPv6 address) = "IPv6 is '" ++ address ++ "'."
checkIP IPv6Localhost = "IPv6, localhost."
Очень глубоко на самом деле.Я давно о подобном думал. А решение оказывается "лежало на поверхности".
вторник, 24 октября 2017 г.
Data 1. Haskell vs мои скрипты
data List = List a List | []
Выглядит так:
Data List
Constructor List (
Anonim ANY
Anonim List
) // List
Constructor [] ()
; // List
Или с именами полей:
Data List
Constructor List (
Named ANY Head
Named List Tail
) // List
Constructor [] ()
; // List
Или в операторном виде:
Data List
Constructor List (
Left Anonim ANY // параметр слева
Anonim List // параметр справа
) // List
Constructor [] ()
; // List
Тогда конструктор List вызывается так:
1 List 2 List []
Строит:
[ 1 2 ]
О! Я понял как pattern matching сделать.
В общем я могу сделать head:tail вместо .Split Head Tail.
aList .Split Head Tail
Можно сделать так (под капотом):
MATCH List:Make ( Head Tail )
Тут говорим, что надо применить pattern matching к конструктору List:Make и положить результаты в ( Head Tail )
А потом MATCH можно спрятать в и вычислять его в зависимости от того с какой стороны от знака равно (=) находится List:Make.
Учитывая, что "экземпляр объкта" знает про конструктор, которым он был создан, то выражение:
MATCH List:Make ( Head Tail )
можно переписать:
MATCH ( Head Tail )
LIST FUNCTION f
LIST IN aList
ANY VAR Head
LIST VAR Tail
aList .Split Head Tail
LIST:Make 1 Map ( 2 * ) Tail
>>> Result
; // f
Можно переписать так:
LIST FUNCTION f
LIST IN aList
ANY VAR Head
LIST VAR Tail
MATCH aList ( Head Tail )
LIST:Make 1 Map ( 2 * ) Tail
>>> Result
; // f
LIST FUNCTION f
LIST IN aList
ANY VAR Head
LIST VAR Tail
MATCH aList
[]
[]
( Head Tail )
( LIST:Make 1 Map ( 2 * ) Tail )
; // MATCH
>>> Result
; // f
Выглядит так:
Data List
Constructor List (
Anonim ANY
Anonim List
) // List
Constructor [] ()
; // List
Или с именами полей:
Data List
Constructor List (
Named ANY Head
Named List Tail
) // List
Constructor [] ()
; // List
Или в операторном виде:
Data List
Constructor List (
Left Anonim ANY // параметр слева
Anonim List // параметр справа
) // List
Constructor [] ()
; // List
Тогда конструктор List вызывается так:
1 List 2 List []
Строит:
[ 1 2 ]
О! Я понял как pattern matching сделать.
В общем я могу сделать head:tail вместо .Split Head Tail.
aList .Split Head Tail
Можно сделать так (под капотом):
MATCH List:Make ( Head Tail )
Тут говорим, что надо применить pattern matching к конструктору List:Make и положить результаты в ( Head Tail )
А потом MATCH можно спрятать в и вычислять его в зависимости от того с какой стороны от знака равно (=) находится List:Make.
Учитывая, что "экземпляр объкта" знает про конструктор, которым он был создан, то выражение:
MATCH List:Make ( Head Tail )
можно переписать:
MATCH ( Head Tail )
Тогда навскидку:
LIST IN aList
ANY VAR Head
LIST VAR Tail
aList .Split Head Tail
LIST:Make 1 Map ( 2 * ) Tail
>>> Result
; // f
Можно переписать так:
LIST FUNCTION f
LIST IN aList
ANY VAR Head
LIST VAR Tail
MATCH aList ( Head Tail )
LIST:Make 1 Map ( 2 * ) Tail
>>> Result
; // f
Или даже так:
LIST IN aList
ANY VAR Head
LIST VAR Tail
MATCH aList
[]
[]
( Head Tail )
( LIST:Make 1 Map ( 2 * ) Tail )
; // MATCH
>>> Result
; // f
Или так:
LIST FUNCTION f
LIST IN aList
MATCH aList
[]
[]
( ANY VAR Head
LIST VAR Tail
Head Tail )
( LIST:Make 1 Map ( 2 * ) Tail )
; // MATCH
>>> Result
; // f
LIST IN aList
MATCH aList
[]
[]
( ANY VAR Head
LIST VAR Tail
Head Tail )
( LIST:Make 1 Map ( 2 * ) Tail )
; // MATCH
>>> Result
; // f
Data. Haskell vs мои скрипты
data MyEnum = One|Two
One == Two
Выглядит так:
Data MyEnum
Constructor One ()
Constructor Two ()
;
One Two ==
One == Two
Выглядит так:
Data MyEnum
Constructor One ()
Constructor Two ()
;
One Two ==
Списки. Haskell vs мои скрипты
f x:xs = 1 : map (*2) xs
Выглядит так:
LIST FUNCTION f
LIST IN aList
ANY VAR Head
LIST VAR Tail
aList .Split Head Tail
LIST:Make 1 Map ( 2 * ) Tail
>>> Result
; // f
У меня нет pattern-matching'а. Поэтому Split.
Ну и да - скрипты у меня конечно императивные. Но с ленивыми вычислениями. И без лишнего копирования списков.
Map ( 2 * ) Tail - не копирует Tail.
LIST:Make - тоже. Там Связный список.
Выглядит так:
LIST FUNCTION f
LIST IN aList
ANY VAR Head
LIST VAR Tail
aList .Split Head Tail
LIST:Make 1 Map ( 2 * ) Tail
>>> Result
; // f
У меня нет pattern-matching'а. Поэтому Split.
Ну и да - скрипты у меня конечно императивные. Но с ленивыми вычислениями. И без лишнего копирования списков.
Map ( 2 * ) Tail - не копирует Tail.
LIST:Make - тоже. Там Связный список.
Про замену .Split написано тут - http://programmingmindstream.blogspot.ru/2017/10/data-haskell-vs.html?m=1
aList .Split Head Tail
Можно сделать так (под капотом):
MATCH List:Make ( Head Tail )
Тут говорим, что надо применить pattern matching к конструктору List:Make и положить результаты в ( Head Tail )
А потом MATCH можно спрятать в и вычислять его в зависимости от того с какой стороны от знака равно (=) находится List:Make.
aList .Split Head Tail
Можно сделать так (под капотом):
MATCH List:Make ( Head Tail )
Тут говорим, что надо применить pattern matching к конструктору List:Make и положить результаты в ( Head Tail )
А потом MATCH можно спрятать в и вычислять его в зависимости от того с какой стороны от знака равно (=) находится List:Make.
понедельник, 23 октября 2017 г.
Haskell. Цитата
"Применение конструктора типа к существующему типу порождает некий новый тип, и это очень мощная техника, используемая в Haskell почти на каждом шагу. Например, если нам нужно завернуть в опциональное значение уже не
String
, а ранее упомянутый Text
, мы ничего не должны менять в конструкторе Maybe"
Вопрос. Haskell
А есть ли "скриптовые" реализации Haskell?
Ну чтобы можно было использовать Haskell в качестве скриптового языка внутри проекта написанного на Delphi.
Ну примерно так, как обычно используют Python.
Ну чтобы можно было использовать Haskell в качестве скриптового языка внутри проекта написанного на Delphi.
Ну примерно так, как обычно используют Python.
воскресенье, 22 октября 2017 г.
Вопрос. Haskell
А кстати можно так написать?
h :: Int -> Int
h x + 1 = x
h 3 == 2
Правило вывода сработает?
Ну это я таким извращённым способом операцию декремента реализую. Теоретически.
Понятно, что можно написать напрямую:
h x = x - 1
но мне интересна внутренняя природа устройства правил вывода.
UPDATE. На вопрос я вроде бы нашёл ответ - в pattern matching'е участвуют не функции, а конструкторы. Поправьте, если я не прав.
h :: Int -> Int
h x + 1 = x
h 3 == 2
Правило вывода сработает?
Ну это я таким извращённым способом операцию декремента реализую. Теоретически.
Понятно, что можно написать напрямую:
h x = x - 1
но мне интересна внутренняя природа устройства правил вывода.
UPDATE. На вопрос я вроде бы нашёл ответ - в pattern matching'е участвуют не функции, а конструкторы. Поправьте, если я не прав.
четверг, 19 октября 2017 г.
first:second:tail
first:second:tail
ARRAY VAR A
VAR first
VAR second
VAR tail
A .Split ( first second tail )
first:second:tail
ARRAY VAR A
VAR first
VAR tail
A .Split ( first tail )
first:second:third:tail
ARRAY VAR A
VAR first
VAR second
VAR third
VAR tail
A .Split ( first second third tail )
ARRAY VAR A
VAR first
VAR second
VAR tail
A .Split ( first second tail )
first:second:tail
ARRAY VAR A
VAR first
VAR tail
A .Split ( first tail )
first:second:third:tail
ARRAY VAR A
VAR first
VAR second
VAR third
VAR tail
A .Split ( first second third tail )
ToDo. Изменение положения оператора
Обратные тики-кавычки нужны для инфиксной записи бинарной функции. То есть мы располагаем mod между аргументами.
1 2 + =>
1 :` + 2
Или:
1 2 + =>
1 ` + 2
operator `
in ValueLeft
^ in Lambda
^ in ValueRight
ValueLeft // - значение слева
ValueRight DO // - разыменовываем значение справа
Lambda DO // - вызываем "лямбду"/"функтор"
;
1 2 + =>
1 :` + 2
Или:
1 2 + =>
1 ` + 2
operator `
in ValueLeft
^ in Lambda
^ in ValueRight
ValueLeft // - значение слева
ValueRight DO // - разыменовываем значение справа
Lambda DO // - вызываем "лямбду"/"функтор"
;
Concat на Haskell
Как нить так (это сходу и наверняка не совсем верно):
concat' [] = []
concat' ([]:ys) = concat' ys
concat' ((x:xs):ys) = x : concat' (xs:ys)
concat' [] = []
concat' ([]:ys) = concat' ys
concat' ((x:xs):ys) = x : concat' (xs:ys)
среда, 18 октября 2017 г.
Вопрос
[ [ 1 2 ] [ 3 4 ] ] как на Haskell преобразовать к [ 1 2 3 4 ] ?
Ну или на каком-нибудь другом функциональном языке?
У меня так:
[ [ 1 2 ] [ 3 4 ] ] .Fold
Но у меня же - "на коленке". Хочется понимать как "у людей" устроено.
А [ 1 2 ] .Join [ 3 4 ] как?
А [ 1 2 ] .Map .ToString как?
А [ 1 2 ] .Filter .IsOdd как?
А 0 [ 1 2 ] .For + /*сумма элементов*/ как?
Надеюсь, что смысл примеров понятен. Хотя они конечно "на коленке".
Если они всё же понятны, то может быть кто-то откликнется и расскажет как подобное на настоящих языках можно сделать?
Ну или на каком-нибудь другом функциональном языке?
У меня так:
[ [ 1 2 ] [ 3 4 ] ] .Fold
Но у меня же - "на коленке". Хочется понимать как "у людей" устроено.
А [ 1 2 ] .Join [ 3 4 ] как?
А [ 1 2 ] .Map .ToString как?
А [ 1 2 ] .Filter .IsOdd как?
А 0 [ 1 2 ] .For + /*сумма элементов*/ как?
Надеюсь, что смысл примеров понятен. Хотя они конечно "на коленке".
Если они всё же понятны, то может быть кто-то откликнется и расскажет как подобное на настоящих языках можно сделать?
ToDo. Сериализация
Похоже, что элементы опубликованные в скрипты автоматом сериализуются. Типа как в json.
Надо эту тему "покурить".
Надо эту тему "покурить".
ToDo
Опубликовать конструкторы от tryFinally и tryExcept.
И ещё ко всем конструкторам сделать "пару" - CreateWithClass/XXXCreateWithClass.
Чтобы через эти конструкторы можно было создавать экземпляры по известной ссылке на класс.
Для финальных классов не делать такую пару.
И ещё ко всем конструкторам сделать "пару" - CreateWithClass/XXXCreateWithClass.
Чтобы через эти конструкторы можно было создавать экземпляры по известной ссылке на класс.
Для финальных классов не делать такую пару.
пятница, 13 октября 2017 г.
Массивы
Range: 0 10 // диапазон от 0 до 10
.Filter .IsOdd // отбираем нечётные
.Join ( // объединение
Range: 20 30 // диапазон от 20 до 30
.Filter . IsEven // отбираем чётные
)
.Join // объединение
[ 31 32 33 ] // массив из элементов 31 32 33
.Join // объединение
ProduceArray: 100 Random // генерируем массив из 100 случайных элементов
.Join // объединение
ProduceArray: 200 ReadIntFromFile: File // читаем 200 элементов из файла
.Join // объединение
[ 100 1024 ] // массив из элементов 100 1024
.Map ( + 1 ) // прибавлям 1
.Map ( * 3 ) // умножаем на 3
.Map .ToString // преобразуем к строке
.Slice 2 // разбиваем массив на пары
.Slice 3 // разбиваем массив на тройки
.Trunc 10 // обрезаем массив до 10 элементов
Array Var A // объявляем переменную типа массив
>>> A // кладём результат вычислений в A
A .Copy // делаем копию массива
>>> A // кладём результат вычислений в A
A .Join A // объединяем A с A
>>> A // кладём результат вычислений в A
A .Fold // преобразуем массив массивов обратно к плоскому списку
.Map .ToInt // преобразуем строки обратно к числам
.RemoveDuplicates // удаляем дубликаты
.Sort // сортируем массив
.Invert // инвертируем массив
.Slice 2 // разбиваем на пары
.Map ( 0 .Swap .For + ) // суммируем пары
.Sort // сортируем
.For // перебираем массив
.Print // печатаем элемент массива
Все вычисления - "ленивые".
Кроме Copy, RemoveDuplicates и Sort.
.Filter .IsOdd // отбираем нечётные
.Join ( // объединение
Range: 20 30 // диапазон от 20 до 30
.Filter . IsEven // отбираем чётные
)
.Join // объединение
[ 31 32 33 ] // массив из элементов 31 32 33
.Join // объединение
ProduceArray: 100 Random // генерируем массив из 100 случайных элементов
.Join // объединение
ProduceArray: 200 ReadIntFromFile: File // читаем 200 элементов из файла
.Join // объединение
[ 100 1024 ] // массив из элементов 100 1024
.Map ( + 1 ) // прибавлям 1
.Map ( * 3 ) // умножаем на 3
.Map .ToString // преобразуем к строке
.Slice 2 // разбиваем массив на пары
.Slice 3 // разбиваем массив на тройки
.Trunc 10 // обрезаем массив до 10 элементов
Array Var A // объявляем переменную типа массив
>>> A // кладём результат вычислений в A
A .Copy // делаем копию массива
>>> A // кладём результат вычислений в A
A .Join A // объединяем A с A
>>> A // кладём результат вычислений в A
A .Fold // преобразуем массив массивов обратно к плоскому списку
.Map .ToInt // преобразуем строки обратно к числам
.RemoveDuplicates // удаляем дубликаты
.Sort // сортируем массив
.Invert // инвертируем массив
.Slice 2 // разбиваем на пары
.Map ( 0 .Swap .For + ) // суммируем пары
.Sort // сортируем
.For // перебираем массив
.Print // печатаем элемент массива
Все вычисления - "ленивые".
Кроме Copy, RemoveDuplicates и Sort.
понедельник, 9 октября 2017 г.
Тут подумалось
Прям самопроцитирую: "Я к тому, что "аллегория" это скажем так "некоторое преобразование" одного более сложного объекта к другому, более простому. Как хеш-функция.
Можно сравнивать строки, а можно их хеш-функции. Хеш-функции сравнивать проще. Но их сравнение менее "однозначно". Ибо там есть "потеря полноты картины". Но зачастую полнота картины и не нужна. Собственно это тогда и есть аллегория.
"
Можно сравнивать строки, а можно их хеш-функции. Хеш-функции сравнивать проще. Но их сравнение менее "однозначно". Ибо там есть "потеря полноты картины". Но зачастую полнота картины и не нужна. Собственно это тогда и есть аллегория.
"
Рекомендую книгу
https://www.ozon.ru/context/detail/id/35009452/
https://m.livelib.ru/work/1002522273/reviews-abstraktnye-tipy-dannyh-v-yazyke-atpaskal-anatolij-dedkov
https://m.livelib.ru/work/1002522273/reviews-abstraktnye-tipy-dannyh-v-yazyke-atpaskal-anatolij-dedkov
четверг, 5 октября 2017 г.
Научился делать "настоящие" immutable объекты
Научился делать "настоящие" immutable объекты.
Через перекрытие NewInstance + VirtualAlloc + VirtualProtect.
Перекрываем NewInstance - там аллоцируем память через VirtualAlloc (там есть тонкости с размером страниц и стратегией выделения памяти).
Потом заполняем поля объекта в конструкторе. И "закрываем" доступ на запись через VirtualProtect + Page_ReadOnly - в AfterCreate.
Потом в FreeInstance (или destroy - тут есть тонкости) опять разрешаем доступ.
Получаем гарантировано неизменяемые объекты.
Например для паттерна flyweight.
Или например для случая:
ConstString = interface
Function S: PChar;
End;
Ничто не мешает сделать так:
Var Cs: ConstString;
Cs := Factory.GetCs('value');
Cs.S^ := 'xxx';
Т.е "кишки" объекта будут испорчены.
А вот virtualProtect + page_readonly - решают эту проблему.
Через перекрытие NewInstance + VirtualAlloc + VirtualProtect.
Перекрываем NewInstance - там аллоцируем память через VirtualAlloc (там есть тонкости с размером страниц и стратегией выделения памяти).
Потом заполняем поля объекта в конструкторе. И "закрываем" доступ на запись через VirtualProtect + Page_ReadOnly - в AfterCreate.
Потом в FreeInstance (или destroy - тут есть тонкости) опять разрешаем доступ.
Получаем гарантировано неизменяемые объекты.
Например для паттерна flyweight.
Или например для случая:
ConstString = interface
Function S: PChar;
End;
Ничто не мешает сделать так:
Var Cs: ConstString;
Cs := Factory.GetCs('value');
Cs.S^ := 'xxx';
Т.е "кишки" объекта будут испорчены.
А вот virtualProtect + page_readonly - решают эту проблему.
Просто
Крысу поймал, телевизор "починил". Теперь бы на работе с заковыристой ошибкой разделаться бы.
Компонент разрушается, но из списка компонент родителя - не удаляется.
А под отладчиком - вроде всё хорошо.
Только под Delphi Tokyo. Под Berlin и Delphi 7 - всё хорошо.
Уже разные способы ловли испробовал. Вплоть до VirtualProtect...
Меняю способ распределения памяти под компонент - ошибка уходит.
Не помогает...
Завтра тупо начну логировать убитые компоненты с их адресами и адресами родителей. И trhreadId.
Смущает тот факт, что ошибка под Tokyo есть, а под Berlin - её нет. Обязательно напишу, если чего накопаю. Хотя конечно скорее всего это какая-то наша ошибка.
Компонент разрушается, но из списка компонент родителя - не удаляется.
А под отладчиком - вроде всё хорошо.
Только под Delphi Tokyo. Под Berlin и Delphi 7 - всё хорошо.
Уже разные способы ловли испробовал. Вплоть до VirtualProtect...
Меняю способ распределения памяти под компонент - ошибка уходит.
Не помогает...
Завтра тупо начну логировать убитые компоненты с их адресами и адресами родителей. И trhreadId.
Смущает тот факт, что ошибка под Tokyo есть, а под Berlin - её нет. Обязательно напишу, если чего накопаю. Хотя конечно скорее всего это какая-то наша ошибка.
среда, 4 октября 2017 г.
Иногда случаются проезды по памяти при использовании getMem/freeMem
Иногда случаются проезды по памяти при использовании getMem/freeMem.
Как их искать?
VirtualAlloc/VirtualProtect далеко не всегда помогают. В силу их специфики.
Как быть?
Придумал вот что:
Const
Filler : array [0..1] of integer = (high(integer), high(integer) -1);
SafeGetMem: Pointer
Var allocated: pointer;
GetMem(result, SizeOf(pointer) + sizeOf(Filler));
GetMem(allocated, aSize + SizeOf(pointer));
Move(result^, result^, sizeOf(result));
Move(result + SizeOf(result)^, Filler, SizeOf(filler));
Move(allocated^, result^, SizeOf(result));
Result := allocated + SizeOf(result);
А в safeFreeMem - проверяем все инварианты.
Как их искать?
VirtualAlloc/VirtualProtect далеко не всегда помогают. В силу их специфики.
Как быть?
Придумал вот что:
Const
Filler : array [0..1] of integer = (high(integer), high(integer) -1);
SafeGetMem: Pointer
Var allocated: pointer;
GetMem(result, SizeOf(pointer) + sizeOf(Filler));
GetMem(allocated, aSize + SizeOf(pointer));
Move(result^, result^, sizeOf(result));
Move(result + SizeOf(result)^, Filler, SizeOf(filler));
Move(allocated^, result^, SizeOf(result));
Result := allocated + SizeOf(result);
А в safeFreeMem - проверяем все инварианты.
ToDo
Сделать newInstance через getMem + offset + high(pointer).
Возможно так можно будет отследить - кто ездит по памяти.
Возможно так можно будет отследить - кто ездит по памяти.
Подписаться на:
Сообщения (Atom)