Страницы

Поиск по вопросам

Показаны сообщения с ярлыком типы-данных. Показать все сообщения
Показаны сообщения с ярлыком типы-данных. Показать все сообщения

суббота, 21 марта 2020 г.

Есть ли смысл использовать собственные реализации базовых АТД в C++?

#cpp #алгоритм #типы_данных #функциональное_программирование #стандарт


Задался вопросом, имеет ли смысл в настоящее время использовать собственные реализации
таких типов данных как связный список/стек/очередь/словарь? Именно для выполнения реальных
задач, а не в учебных целях. Казалось бы, стандарт языка С++ на данный момент предоставляет
большое количество инструментов, но несмотря на это, в некоторых проектах всё ещё продолжают
использовать самописные реализации тех или иных АТД. В том редком случае, когда стандартные
средства уступают в производительности самописным, логично, что выбор вряд ли будет
в пользу стандартных. Но в большинстве случаев нет какой-либо критичной разницы между
двумя подходами. Так стоит ли игра свеч?
    


Ответы

Ответ 1



Реализация контейнеров и алгоритмов в STL или Boost - веселая штука. Их делают максимально унифицированными (в основном для покрытия максимального кол-ва задач) и очень тщательно проверяют на быстродействие и т.д. Их пишут не один десяток людей, а ревьювят его вообще все ). Это дает гарантию что конкретная реализация будет максимально удовлетворять потребностям большинства. Время когда люди писали свои "общие" контейнера и алгоритмы наверное прошло. Но во в задачах (пример: Вам нужен произвольный доступ, и ассоциотивность) - да надо писать свой костыль. В стандартах очень много ассемблерных вставок для оптимизации производительности, и поэтому я не думаю что компилятор родит более вменяемый код, чем тот над которым посидели оптимизаторы. ИМХО: В реальных задачах надо брать готовое, а не рожать что-то (ибо дорого) сейчас вообще большинство ничего не кодит (дешевле найти готовое и пришить), что разумеется пичалька

понедельник, 24 февраля 2020 г.

Python, байты и числа в integer

#python #python_3x #типы_данных


Осваиваю постепенно пайтон, пишу программу для общения с AVR контроллером через блютуз.

Из сокета я получаю данные в виде строки байт такого вида b'\xb1\xaa\xab\xac\xad\xae\xaf'.

Так как я пишу свой протокол и работаю с разными типами данных (отправляю их с МК
не в символьном виде), разбиваю строку на части, к примеру: b'\xb1', b\xaa\xab' и b\xac\xad\xae\xaf',
то есть на однобайтовый int, двухбайтовый int и четырёхбайтовый int, с чем проблем
не возникает.

Главная задача - преобразовать эти байтовые последовательности в исходные числа.
Однобайтовый int вполне получается преобразовать так:

In [64]: line = b'\t\xaa\xab\xac\xad\xae\xaf'
In [65]: line [0]
Out[65]: 9
In [72]: a = line [0]

In [73]: a
Out[73]: 9

In [74]: a += 1

In [75]: a
Out[75]: 10


Но вот метод преобразования в число двух и более бай мне неведом. В результате поиска
больше запутался.
И так, вопрос: как имея в исходных данных байтовые строки вида b\xaa\xab' и b\xac\xad\xae\xaf'
привести их к целочисленному типу данных?
    


Ответы

Ответ 1



Чтобы конвертировать байты в целое число в Питоне 3 в независимости от числа байт: >>> int.from_bytes(b'\xb1', 'big') 177 >>> int.from_bytes(b'\xaa\xab', 'big') 43691 >>> int.from_bytes(b'\xac\xad\xae\xaf', 'big') 2897063599 >>> int.from_bytes(b'\xac\xad\xae\xaf', 'big', signed=True) -1397903697 >>> int.from_bytes(b'\xac\xad\xae\xaf', 'little', signed=True) -1347506772 >>> int.from_bytes(b'\xac\xad\xae\xaf', 'little') 2947460524 Если много однотипных данных, можно array.array, numpy.array использовать для экономии памяти и быстроты работы векторных операций. Если есть обрамляющая структура, то можно прочесть данные, используя struct.Struct, ctypes.Structure (для struct из С).

Ответ 2



Посмотрите в сторону модуля struct, он как раз для этих задач. В Вашем случае: import struct line = b'\xaa\xab\xac\xad\xae\xaf' format = '

пятница, 21 февраля 2020 г.

Вопрос по указателям в Golang

#golang #указатели #типы_данных


Недавно начал изучать язык программирования Golang, не очень по началу хотелось,
но по работе нужно было, после программирования на Java чуток сложновато.
Появилось пару вопросов, в языке Golang простые типы передаются по значению, так
же к простому типу относятся и массивы, maps и slices передаются по ссылке, структуры
я понял можно передавать как по ссылке так и по значению, все зависит от разработчика,
но часто натыкаюсь на примеры в нете где слайсы и мапы передают через взятие адреса,
зачем это нужно, если по умолчанию, они все равно так и возвращаются, тем более если
передать слайс явно по ссылке то его уже нельзя итерировать, если я что-то не правильно
понял объясните, очень мало документации на русском.
    


Ответы

Ответ 1



В golang нет передачи параметров по ссылке - параметры всегда передаются по значению, т.е. копируются. Оператор & - это взятие адреса переменной, т.е. передача указателя, а не передача параметра по ссылке, т.е. как частный случай по значению передаются указатели. map, slice - тоже передаются по значению, просто у них внутри указатели и происходит копирование указателей, а не содержимого таблицы/среза. Проще всего это продемонстрировать на примере slice (среза). Внутри slice представляет собот структуру: https://github.com/golang/go/blob/master/src/runtime/slice.go type slice struct { array unsafe.Pointer len int cap int } т.е. при передаче по значению - можно изменять элементы внутри slice.array, т.к. указатель после копирования будет указывать на ту же область памяти что и исходный, но вот изменить длину или вместимость срезу уже не получится. Точнее эти изменения не будут видны снаружи вызываемой функции, т.к. длина, вместимость и (возможно) указатель array изменятся только в копии среза. Вот наглядный пример: https://play.golang.org/p/SpFFDdzeXR package main import ( "fmt" ) func f_slice_item(s []int) { s[0] = 1 } func f_slice_size(s []int) { s = append(s, 1) } func f_slice_pointer_item(s *[]int) { (*s)[0] = 3 } func f_slice_pointer_size(s *[]int) { *s = append(*s, 5) } func f_iter(s *[]int) { for i, v := range *s { fmt.Println(i, "-", v) } } func main() { var s []int fmt.Println("slice by value") s = []int{0} f_slice_item(s) fmt.Println(s) s = []int{0} f_slice_size(s) fmt.Println(s) fmt.Println() fmt.Println("slice by pointer") s = []int{0} f_slice_pointer_item(&s) fmt.Println(s) s = []int{0} f_slice_pointer_size(&s) fmt.Println(s) fmt.Println() fmt.Println("iter") f_iter(&s) } Результат выполнения: slice by value [1] [0] slice by pointer [3] [0 5] iter 0 - 0 1 - 5

вторник, 18 февраля 2020 г.

Динамическое установление типа в зависимости от условия

#c_sharp #типы_данных


Есть две сборки (service reference) в одном namespace OrderSendService. Обе имеют
класс Order:

public class Order
{
   public int id { get; set; }
   public string type { get; set; }
}


ServiceReference1 и ServiceReference2, это Service references, полученные на основе
wsdl. Мне нужно использовать оба этих референса. Сейчас  формируется объект  ServiceReference1.Order
и отправляется методом ServiceReference1.Send(). Возникла необходимость для отправки
использовать ServiceReference2.Send() и передавать этот же объект (при этом ServiceReference2.Send()
принимает объекты из своего namespace)

Возможно ли в зависимости от условия, установить тип объекту? Использую такой код:

dynamic obj;
if (condition)
{
   obj = new ServiceReference1.Order[10];
}
else
{
   obj = new ServiceReference2.Order[10];
}


(obj будет передаваться в методе Send() одного из wsdl-сервисов).

Неймспейсы сервисов:

OrderSendService.ServiceReference1
OrderSendService.ServiceReference2


При попытке доступа к свойству obj[0].id VS 2015 показывает ошибку:


  Missing compiler required member
  Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create


И возможно ли привести тип ServiceReference1.Order к ServiceReference2.Order?
    


Ответы

Ответ 1



Судя по всему, не хватало подключения библиотеки Microsoft.CSharp. Поскольку dynamic — внутри-C#-овская фича, ей нужна специальная поддержка. Проекты, созданные старыми версиями компилятора, не содержат ссылки на Microsoft.CSharp, поэтому ссылку иногда приходится добавлять вручную.

Ответ 2



Заведите для разных служб разные пространства имен. Вы искуственно создаете ситуацию, когда .NET не имеет возможности различить два типа Order в том месте кода, где они "видны". И возможно ли привести тип ServiceReference1.Order к ServiceReference2.Order Нет. public class OrderConverter { public static ServiceReference1.Order Convert(ServiceReference2.Order anOrder) { return new ServiceReference1.Order() { id = anOrder.id; type = anOrder.type; }; } public static ServiceReference2.Order Convert(ServiceReference1.Order anOrder) { return new ServiceReference2.Order() { id = anOrder.id; type = anOrder.type; }; } }

воскресенье, 16 февраля 2020 г.

Python, байты и числа в integer

#python #python_3x #типы_данных


Осваиваю постепенно пайтон, пишу программу для общения с AVR контроллером через блютуз.

Из сокета я получаю данные в виде строки байт такого вида b'\xb1\xaa\xab\xac\xad\xae\xaf'.

Так как я пишу свой протокол и работаю с разными типами данных (отправляю их с МК
не в символьном виде), разбиваю строку на части, к примеру: b'\xb1', b\xaa\xab' и b\xac\xad\xae\xaf',
то есть на однобайтовый int, двухбайтовый int и четырёхбайтовый int, с чем проблем
не возникает.

Главная задача - преобразовать эти байтовые последовательности в исходные числа.
Однобайтовый int вполне получается преобразовать так:

In [64]: line = b'\t\xaa\xab\xac\xad\xae\xaf'
In [65]: line [0]
Out[65]: 9
In [72]: a = line [0]

In [73]: a
Out[73]: 9

In [74]: a += 1

In [75]: a
Out[75]: 10


Но вот метод преобразования в число двух и более бай мне неведом. В результате поиска
больше запутался.
И так, вопрос: как имея в исходных данных байтовые строки вида b\xaa\xab' и b\xac\xad\xae\xaf'
привести их к целочисленному типу данных?
    


Ответы

Ответ 1



Чтобы конвертировать байты в целое число в Питоне 3 в независимости от числа байт: >>> int.from_bytes(b'\xb1', 'big') 177 >>> int.from_bytes(b'\xaa\xab', 'big') 43691 >>> int.from_bytes(b'\xac\xad\xae\xaf', 'big') 2897063599 >>> int.from_bytes(b'\xac\xad\xae\xaf', 'big', signed=True) -1397903697 >>> int.from_bytes(b'\xac\xad\xae\xaf', 'little', signed=True) -1347506772 >>> int.from_bytes(b'\xac\xad\xae\xaf', 'little') 2947460524 Если много однотипных данных, можно array.array, numpy.array использовать для экономии памяти и быстроты работы векторных операций. Если есть обрамляющая структура, то можно прочесть данные, используя struct.Struct, ctypes.Structure (для struct из С).

Ответ 2



Посмотрите в сторону модуля struct, он как раз для этих задач. В Вашем случае: import struct line = b'\xaa\xab\xac\xad\xae\xaf' format = '

Вопрос по указателям в Golang

#golang #указатели #типы_данных


Недавно начал изучать язык программирования Golang, не очень по началу хотелось,
но по работе нужно было, после программирования на Java чуток сложновато.
Появилось пару вопросов, в языке Golang простые типы передаются по значению, так
же к простому типу относятся и массивы, maps и slices передаются по ссылке, структуры
я понял можно передавать как по ссылке так и по значению, все зависит от разработчика,
но часто натыкаюсь на примеры в нете где слайсы и мапы передают через взятие адреса,
зачем это нужно, если по умолчанию, они все равно так и возвращаются, тем более если
передать слайс явно по ссылке то его уже нельзя итерировать, если я что-то не правильно
понял объясните, очень мало документации на русском.
    


Ответы

Ответ 1



В golang нет передачи параметров по ссылке - параметры всегда передаются по значению, т.е. копируются. Оператор & - это взятие адреса переменной, т.е. передача указателя, а не передача параметра по ссылке, т.е. как частный случай по значению передаются указатели. map, slice - тоже передаются по значению, просто у них внутри указатели и происходит копирование указателей, а не содержимого таблицы/среза. Проще всего это продемонстрировать на примере slice (среза). Внутри slice представляет собот структуру: https://github.com/golang/go/blob/master/src/runtime/slice.go type slice struct { array unsafe.Pointer len int cap int } т.е. при передаче по значению - можно изменять элементы внутри slice.array, т.к. указатель после копирования будет указывать на ту же область памяти что и исходный, но вот изменить длину или вместимость срезу уже не получится. Точнее эти изменения не будут видны снаружи вызываемой функции, т.к. длина, вместимость и (возможно) указатель array изменятся только в копии среза. Вот наглядный пример: https://play.golang.org/p/SpFFDdzeXR package main import ( "fmt" ) func f_slice_item(s []int) { s[0] = 1 } func f_slice_size(s []int) { s = append(s, 1) } func f_slice_pointer_item(s *[]int) { (*s)[0] = 3 } func f_slice_pointer_size(s *[]int) { *s = append(*s, 5) } func f_iter(s *[]int) { for i, v := range *s { fmt.Println(i, "-", v) } } func main() { var s []int fmt.Println("slice by value") s = []int{0} f_slice_item(s) fmt.Println(s) s = []int{0} f_slice_size(s) fmt.Println(s) fmt.Println() fmt.Println("slice by pointer") s = []int{0} f_slice_pointer_item(&s) fmt.Println(s) s = []int{0} f_slice_pointer_size(&s) fmt.Println(s) fmt.Println() fmt.Println("iter") f_iter(&s) } Результат выполнения: slice by value [1] [0] slice by pointer [3] [0 5] iter 0 - 0 1 - 5

Динамическое установление типа в зависимости от условия

#c_sharp #типы_данных


Есть две сборки (service reference) в одном namespace OrderSendService. Обе имеют
класс Order:

public class Order
{
   public int id { get; set; }
   public string type { get; set; }
}


ServiceReference1 и ServiceReference2, это Service references, полученные на основе
wsdl. Мне нужно использовать оба этих референса. Сейчас  формируется объект  ServiceReference1.Order
и отправляется методом ServiceReference1.Send(). Возникла необходимость для отправки
использовать ServiceReference2.Send() и передавать этот же объект (при этом ServiceReference2.Send()
принимает объекты из своего namespace)

Возможно ли в зависимости от условия, установить тип объекту? Использую такой код:

dynamic obj;
if (condition)
{
   obj = new ServiceReference1.Order[10];
}
else
{
   obj = new ServiceReference2.Order[10];
}


(obj будет передаваться в методе Send() одного из wsdl-сервисов).

Неймспейсы сервисов:

OrderSendService.ServiceReference1
OrderSendService.ServiceReference2


При попытке доступа к свойству obj[0].id VS 2015 показывает ошибку:


  Missing compiler required member
  Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create


И возможно ли привести тип ServiceReference1.Order к ServiceReference2.Order?
    


Ответы

Ответ 1



Судя по всему, не хватало подключения библиотеки Microsoft.CSharp. Поскольку dynamic — внутри-C#-овская фича, ей нужна специальная поддержка. Проекты, созданные старыми версиями компилятора, не содержат ссылки на Microsoft.CSharp, поэтому ссылку иногда приходится добавлять вручную.

Ответ 2



Заведите для разных служб разные пространства имен. Вы искуственно создаете ситуацию, когда .NET не имеет возможности различить два типа Order в том месте кода, где они "видны". И возможно ли привести тип ServiceReference1.Order к ServiceReference2.Order Нет. public class OrderConverter { public static ServiceReference1.Order Convert(ServiceReference2.Order anOrder) { return new ServiceReference1.Order() { id = anOrder.id; type = anOrder.type; }; } public static ServiceReference2.Order Convert(ServiceReference1.Order anOrder) { return new ServiceReference2.Order() { id = anOrder.id; type = anOrder.type; }; } }

четверг, 13 февраля 2020 г.

Приведение строки к nullable int типу

#c_sharp #net #типы_данных


Подскажите, можно ли без написания функций привести строку к ?int типу ?

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

Неужели средствами самого C# нельзя выполнить подобные преобразования?
    


Ответы

Ответ 1



Попробуйте int.Parse. Или int.TryParse, если строка имеет законное право быть не строковым представлением числа. Скорее всего, вам понадобится вариант с указанием языка: int.Parse(s, CultureInfo.InvatiantCulture) или int.TryParse(s, NumberStyles.Integer, CultureInfo.InvatiantCulture, out result). Для случая nullable-типа, если вам нужно получить null при ошибке преобразования, придётся-таки вручную: int temp; int? result = int.TryParse(s, out temp) ? temp : default(int?);

понедельник, 10 февраля 2020 г.

Как происходит расширение примитивных типов

#java #типы_данных


Предположим, у нас есть такой код:   

long a = 0x1_0000_0000L + 0xCAFE_BABE;
System.out.println(a == 0x1_CAFE_BABEL);


Почему будет выведен false? По идее, число 0xCAFE_BABE должно быть автоматически
расширено до типа long, и уже после этого должно произойти сложение.
    


Ответы

Ответ 1



а оно и происходит. Просто 0xCAFE_BABE - отрицательное (!) поэтому оно и приводится к отрицательному long - у. Т.е. (long)(0xCAFE_BABE) (именно в такой записи) - это FFFF_FFFF_CAFE_BABE что немного не то, на что вы рассчитывали. Поэтому числа больше 7FFF_FFFF в подобных ситуациях нужно писать только с суффиксом L.

воскресенье, 9 февраля 2020 г.

Дополнительные типы данных в C

#c #типы_данных


Я хочу узнать что за типы такие: uint8_t, uint16_t и т. д. Слышал, что это тоже самое,
что и unsigned char, unsigned short и т. д.

Но в чем на самом деле разница? Созданы ли эти типы специально чтобы вне зависимости
от платформы выделять для переменных определенное количество байт?

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


Ответы

Ответ 1



Типы int16_t, uint16_t, … — это не совсем то же самое, что и базовые целочисленные типы (англ. basic integer types) в C (char, int, short и т. д.). Основное отличие заключается в том, что для «обычных» типов стандарт определяет только одно — диапазон допустимых значений. Больше никаких требований на эти типы стандарт не налагает. Они могут быть сколь угодно широкие, их внутреннее представление может быть каким угодно, главное, чтобы сохранялась относительная ширина типов и диапазон допустимых значений. Это все, что требует стандарт. И именно эта нетребовательность стандарта позволяет быть этим типам максимально переносимыми. Другое дело — типы из . Они были введены в C99 и называются фиксированными целочисленными типами (англ. fixed integer types). На такие типы стандарт налагает гораздо больше требований. Например типы определенной ширины (англ. exact-width integer types) (int8_t, int16_t, int32_t и int64_t) должны представляться в дополнительном коде, иметь ширину в 8, 16, 32 и 64 бита соответственно и не должны содержать padding bits. Также определяются типы с шириной не меньше гарантированной; максимально широкие типы; типы, работа с которым максимально быстрая и т. д. Чувствуете эту разницу? На последние стандарт налагает гораздо больше требований, что делает их гораздо менее портабельными, но дает разработчику гораздо большую гибкость. Что, если мне нужен целочисленный тип, шириной ровно в 8 бит, или с шириной не меньше 32 бита? Стандарт предоставляет мне такую возможность на уровне языка. Возможность использования таких типов в конкретном случае зависит лишь от реализации. Вдруг, на некоторой машине в принципе не существует типов ровно в 8 бит длиной? Стандарт как бы говорит: «Нужно целочисленное число ровно в 8 бит шириной — пожалуйста, используйте int8_t, если конечно ваша реализация его поддерживает». size_t Тип size_t нужен для одного — представлять размеры объектов. Этот тип может представить максимально возможный размер любого объекта. Одно из следствий такого «свойства» — это то, что size_t — единственный тип, для которого гарантируется, что он может хранить любой возможный индекс массива (В следствии того, что массив в C — это объект). Из этого следствия вытекает частое применение этого типа как типа для различных счетчиков в цикле, которые перебирают массивы. И это единственный подходящий тип для данной задачи! Создатели языка решили определить отдельный тип для такой задачи (представление размера объектов). И вот почему. Представим, что у нас вообще нет такого типа, как size_t. И у нас стоит задача написать свой функцию strlen с использованием, например, цикла for: for (... i = 0; str[i] != '\0'; ++i) Какой тип мы будем использовать для счетчика? Может int? Но это неэффективно, так как индекс не может быть отрицательным. Тогда, пожалуй, unsigned int. Уже лучше, но что, если наша строка длиннее, чем UINT_MAX? Тогда возьмем unsigned long! Но, что если максимальная длина строки на платформе гораздо меньше ULONG_MAX и настолько большой тип будет неэффективен?.. Чтобы разработчику в такой ситуации не гадать, что да как, и был введен тип size_t.

Ответ 2



Слышал, что это тоже самое что и unsigned char, unsigned short и т.д. Но в чем на самом деле разница? Это не всегда то же самое: Есть системы где например тип char беззнаковый - соотвественно uint_8 это алиас на char а не на unsigned char как можно было бы логично догадаться. Созданы ли эти типы специально чтобы вне зависимости от платформы выделять для переменных определенное количество байт? Типы uint[N]_t это беззнаковые типа гарантирующиеся быть размером в точности N бит, в то время как размеры встроенных типов unsigned char unsigned short unsigned int ... могут разнится в зависимости от реализации и платформы (а не только от платформы). Ну а вообще, надёжнее всего найти этот алиас в коде хедера и вывести его размер через стандартный sizeof оператор - так вы узнаете размер под вашу конкретную платформу и реализацию. Также хотелось бы узнать про size_t. Я знаю, что он возвращается функцией sizeof, но в чем была необходимость его создания? Цитата из вики о size_t size_t — беззнаковый целый тип, предназначенный для представления размера любого объекта в памяти (включая массивы) в конкретной реализации. Оператор sizeof возвращает значение типа size_t. Максимальный размер size_t записан в макроконстанте SIZE_MAX, определённой в (cstdint для C++). size_t должен быть, как минимум, 16 бит. К тому же POSIX включает ssize_t, который является встроенным знаковым типом, по размеру равным size_t.

суббота, 1 февраля 2020 г.

Как красиво передать slice с разными скалярными типами в функцию?

#golang #типы_данных


Есть функция, которая обрезает slice со второго элемента и принимает аргумент с типом
[]interface{}

func SliceShift(slice []interface{}) []interface{} {
    if len(slice) > 1 {
        return slice[1:]
    }
    return make([]interface{}, 0)
}


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

SliceShift([]string{"a", "b", "c"})


то получаю ошибку: cannot use []string literal (type []string) as type []interface
{} in argument to SliceShift.
Не очень понимаю почему она происходит, но нагулил решение:

str := []string{"a", "b", "c"}
newStr := make([]interface{}, len(str))
for i, v := range str {
    newStr[i] = v
}


Проблема в том, что мне не хочется каждый раз писать make([]interface{}, ...) и цикл
копирования.
Можно ли это как-то избежать?
    


Ответы

Ответ 1



API довольно странное и будет и далее порождать подобные проблемы. Делайте хелпер: type any = interface{} func ss2anys(ss []string) (anys []any) { anys = make([]any, len(ss)) for i := range ss { anys[i] = ss[i] } return anys } Playground: https://play.golang.org/p/B2xyfjUYYQu.

Ответ 2



Из faq по golang Can I convert a []T to an []interface{}? Not directly. It is disallowed by the language specification because the two types do not have the same representation in memory. It is necessary to copy the elements individually to the destination slice. This example converts a slice of int to a slice of interface{}: t := []int{1, 2, 3, 4} s := make([]interface{}, len(t)) for i, v := range t { s[i] = v } Поэтому тут либо функции хелперы под каждый тип, либо так еще можно https://play.golang.org/p/zwqv9ulWW_B package main import ( "fmt" "log" ) func main() { fmt.Printf("String test: %v\n", SliceShift([]string{"a", "b", "c"})) fmt.Printf("String test: %v\n", SliceShift([]string{})) fmt.Printf("Int test: %v\n", SliceShift([]int{1, 2, 3, 4, 5})) names := []string{"Igor", "John", "Garry"} names = SliceShift(names).([]string) fmt.Printf("Names test: %v\n", names) } func SliceShift(slice interface{}) interface{} { switch v := slice.(type) { case []string: if len(v) > 1 { return v[1:] } return make([]string, 0) case []int: if len(v) > 1 { return v[1:] } return make([]int, 0) default: log.Panicf("unknown slice type %#v", v) } return make([]interface{}, 0) }

Ответ 3



Попробуйте так : func SliceShift(slice interface{}) interface{} { s := slice.([]string) return s[1:] } func main() { fmt.Println(SliceShift([]string{"a", "b", "c"})) } вот рабочий пример если в функцию нужно передавать разные типы (можно сделать в SliceShift 2й аргумент который будет указывать тип) и внутри функции проверку к какому типу преобразовать. Вот пример: func SliceShift(slice interface{}, tp int) interface{} { switch (tp) { case 0: return slice.([]string)[1:] case 1: return slice.([]uint64)[1:] case 2: return slice.([]byte)[1:] case 3: return slice.([]float32)[1:] } return struct{}{} } func main() { fmt.Println(SliceShift([]byte("test"), 2)) fmt.Println(SliceShift([]string{"a", "b", "c"}, 0)) } только учтите что передав не корректные типы будет паника.

среда, 29 января 2020 г.

В переменную int некорректно записывается произведение двух больших чисел

#cpp #типы_данных #арифметика


long long int var=100000*100000;


В var оказывается 1410065408, хотя 100000^2=10 000 000 000. Почему так?
    


Ответы

Ответ 1



Потому что числа - int, умножаются, как int, и дают в результате int с урезанным переполнением: 10000000000 в шестнадцатеричной записи - 2540BE400. Обрезая до 4 байт, имеем 540BE400, или 1410065408. Попробуйте явно указать, что вы умножаете long long'и - не пожалеете :) long long int var=100000ll*100000ll;

Ответ 2



В таких ситуациях для избежания переполнения и обеспечения типонезависимости кода можно порекомендовать реализовывать операцию умножения в два этапа long long int var = 100000; var *= 100000; Это позволить вам избежать ненужного повторения явной типизации, т.е. явной привязки ваших констант к конкретным типам. Альтернативным вариантом, уместным в некоторых контекстах, будет перенести явную типизацию в правую часть auto var = 100000ll * 100000;

Что означает данное приведение типа

#cpp #типы_данных #преобразование_типов


В статье в по адресу https://ru.cppreference.com/w/cpp/algorithm/transform наткнулся
на такое приведение типа:

(int (*)(int))std::toupper


Как правильно прочитать данное приведение типа, и для чего оно нужно.
    


Ответы

Ответ 1



int (*)(int) - указатель ((*)) на функцию от int ((int)), которая возвращает int. int (*)(int) ^ указатель ^ на функцию от int ^ возвращающую int

вторник, 7 января 2020 г.

Функция, возвращающая значения разных типов

#функции #типы_данных #rust


Как можно написать функцию, которая при определенных условиях возвращает значения
разных типов?

Например:

fn function(type_var:&str) {
   if type_var == "i32" {
      return 50i32;
   } else if type_var == "f64" {
      return 50.0;
   } else {
      return '50';
   }

    


Ответы

Ответ 1



В Rust строгая типизация, поэтому тип возвращаемого значения должен быть какой-то один. Тем не менее, можно создать тип-перечисление с различными типами вложенных значений в вариантах: enum ParsedValue { Int32(i32), Float(f64), String(String) } (последний вариант типа String с динамическим выделением и копированием строки, чтобы не отвлекаться на параметры времени жизни; если нужен пример не-аллоцирующей функции, могу развить). Функция в примере будет выглядеть так: fn function(type_var: &str) -> ParsedValue { if type_var == "i32" { ParsedValue::Int32(50) } else if type_var == "f64" { ParsedValue::Float(50.0) } else { ParsedValue::String(String::from("50")) } } По значению ParsedValue все равно придется делать что-нибудь вроде match, чтобы деструктурировать значение варианта, но этот match обязан быть исчерпывающим.

Преобразование типов в C++

#cpp #типы_данных #преобразование


Как в C++ происходит преобразование типов при присвоении беззнаковому типу отрицательного
числа или числа не из диапазона типа? 
    


Ответы

Ответ 1



При выполнении присваивания производится преобразование исходного значения к типу переменной-приемника. Поведение определяется правилами таких преобразований. Переполнение при преобразовании в беззнаковый целый тип из целых типов (как знаковых, так и беззнаковых) обрабатывается по правилами модульной арифметики с модулем 2^N, где N количество значащих бит в целевом беззнаковом типе. Переполнение при преобразовании в знаковый целый тип из целых типов (как знаковых, так и беззнаковых) приводит к поведению, определяемому реализацией. Реализация в том числе имеет право в таких ситуациях выкидывать сигнал. Переполнение при преобразовании в целый тип из плавающих типов приводит к неопределенному поведению. Переполнение при преобразовании в плавающий тип приводит к неопределенному поведению.

Ответ 2



Данные преобразования описаны в разделе 4.7 Integral conversions стандарта C++. В этом разделе в отношении преобразования из знакового целочисленного типа в беззнаковый целочисленный тип написано 2 If the destination type is unsigned, the resulting value is the least unsigned integer congruent to the source integer (modulo 2n where n is the number of bits used to represent the unsigned type). [ Note: In a two’s complement representation, this conversion is conceptual and there is no change in the bit pattern (if there is no truncation). —end note ] Что касается преобразования в знаковый целочисленный тип, то там же написано 3 If the destination type is signed, the value is unchanged if it can be represented in the destination type (and bit-field width); otherwise, the value is implementation-defined. Если же объекты знаковых и беззнаковых целых чисел принимают участие в выражении, то сначала определяется их общий тип, чтобы вывести тип значения выражения, согласно правилам обычных арифметических преобразований. Например, согласно этим правилам если два целочисленных типа, беззнаковый и знаковый, имеют одинаковый ранг, то объект знакового типа преобразуется к беззнаковому типу. Вот пример, на который не каждый программист сможет сходу ответить. Допустим у вас на машине sizeof( long ) равняется sizeof( int ). Можно предположить, что оба эти выражения равны 4, хотя это не обязательно. И имеются следующие объявления unsigned int x = 0; long y = 0; Спрашивается: какой тип будет иметь выражение:) x + y

понедельник, 6 января 2020 г.

Switch использование readonly переменных в case

#c_sharp #типы_данных


Имею следующий код

static void Main(string[] args)
        {
            string value = "value1";
            // switch с const переменными работает
            switch(value)
            {
                case ConstVariable.VALUE1:
                    Console.WriteLine(ConstVariable.VALUE1);
                    break;
                case ConstVariable.VALUE2:
                    Console.WriteLine(ConstVariable.VALUE2);
                    break;
                case ConstVariable.VALUE3:
                    Console.WriteLine(ConstVariable.VALUE3);
                    break;
            }

            // ЗДЕСЬ ОШИБКА!!! switch c readonly переменными не работает
            switch (value)
            {
                case ReadOnlyVariable.VALUE1:
                    Console.WriteLine(ConstVariable.VALUE1);
                    break;
                case ReadOnlyVariable.VALUE2:
                    Console.WriteLine(ConstVariable.VALUE2);
                    break;
                case ReadOnlyVariable.VALUE3:
                    Console.WriteLine(ConstVariable.VALUE3);
                    break;
            }
        }


        static class ReadOnlyVariable
        {
            public static readonly string VALUE1 = "value1";

            public static readonly string VALUE2 = "value2";

            public static readonly string VALUE3 = "value3";

        }

        static class ConstVariable
        {
            public const string VALUE1 = "value1";

            public const string VALUE2 = "value2";

            public const string VALUE3 = "value3";

        }


В блоке switch я могу использовать переменные const, но если я использую переменны
readonly, то получаю ошибку


  A constant value is expected


Почему так, разве переменные readonly на этапе компиляции не определяются?
    


Ответы

Ответ 1



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

четверг, 2 января 2020 г.

Локальные переменные в Java

#java #ооп #переменные #методы #типы_данных


Собственно говоря, суть моего вопроса можно свести к следующему: почему мы в обязательном
порядке должны присваивать начальное значение локальным переменным, чтобы у нас появилась
возможность их как бы то ни было использовать? Почему для локальных переменных отсутствуют
значения по умолчанию? С чем это может быть связано? 

И ещё один интересный момент, который напрямую связан с моделью памяти в языке программирования
Java. На каком этапе локальная переменная добавляется в стэк? На этапе объявления?
Или всё же на этапе присваивания начального значения? Ведь если мы изначально выделяем
какие-нибудь 4 байта для хранения значения типа int, то конкретное значение у нас уже
имеется (его будет составлять совокупность двоичных разрядов, которые мы уже зарезервировали
под переменную). В общем, хотелось бы разобраться во всех этих вопросах. Всем крайне
благодарен!
    


Ответы

Ответ 1



У компилятора просто нет возможности определить обращаешься ты к проинициализированой переменной в классе или нет, потому на всякий случай им даются стандартные значения. А вот для локальных переменных можно отследить, что она не была проинициализирована и кинуть ошибку. Изначально, объявив переменную это всего лишь указатель на участок памяти, и содержит она то, что было в этом участке памяти до этого, то есть мусор. Чтобы мусора там не было, переменной нужно присвоить значение. То есть, стандартное значение у переменных класса это скорее костыль, для избежания получения "мусора" из переменной. А для локальных можно кинуть ошибку на стадии компиляции

суббота, 28 декабря 2019 г.

Минимизация потребления памяти корректировкой типов

#c #оптимизация #память #типы_данных


Работаю с проектом, реализующим кодирование информации. В коде имеются достаточно
большие двумерные массивы. Вопрос в следующем: есть ли смысл минимизировать потребление
памяти, прицельно используя специальные типы данных?
Пример 1: int stateTable[64][6]; а там, в этой таблице, сплошь нули и единицы. Нужно
ли приводить к виду uint8_t stateTable[64][6];? При этом придётся перелопачивать весь
проект и править все типы во всех методах, где используется эта таблица.
Пример 2: for(int i; i < N; ++i) - а по логике работы больше итератор не может быть
больше 64. Нужно ли "забирать" у него лишнюю память?
Стоят ли подобные ухищрения времени, или это незначащие мелочи?
    


Ответы

Ответ 1



64*6*4 = 1536 - полтора килобайта. Я бы даже не переживал за такие размеры. Но если эту таблицу нужно постоянно копировать туда-сюда, а там на самом деле ноли и единицы, то может и упаковать ее (используя битовые операции) При этом придётся перелопачивать весь проект и править все типы во всех методах, где используется эта таблица. Для этого используют typedef. Заводят свой новый тип и его уже используют. Это позволит и посмотреть на реальное использование. Да и просто обычными ifdef можно будет легко переключать. for(int i; i < N; ++i) А вот здесь все куда интереснее. Вы можете конечно написать uint8_t (и не забыть инициализировать i, а то мало чего). Но вот только это не улучшит ситуацию. Компилятор скорее всего забросит i в регистр, где оно будет как минимум 32бита. А если использовать uint8_t, то компилятор может взять и начать делать касты (но может и не начать). Что бы не гадать, в stdint.h завезли специальные типы для этого случая. Вам наверно подойдет 'uint_fast8_t'. На моей машине внутри этот тип определен так typedef unsigned char uint_fast8_t; (я думал, там будет unsigned int). Но вот использовать этот тип как элемент массива я уже бы не стал - обычно все алгоритмы кодирования информации очень трепетно к размерам относятся, а здесь может от компилятора к компилятору меняться. Итого. Мое предложение - для всех "сомневающихся случаев" заведите свой тип, объявите его в одном глобальном хедере и просто используйте. Если что то поменяется - так будет проще.

Ответ 2



Я бы смотрел, что важнее - скорость или память. Битовые операции просто потребуют лишних вычислений. Байты - тоже не самые быстрые по скорости. int обычно быстрее (см., например, сравнение в этом ответе). Поэтому я бы перед окончательным принятием решения проводил эксперименты... А для облегчения их проведения - действительно, использовал бы typedef, как советует @KoVadim.

Ответ 3



Смысл минимизировать потребление памяти, прицельно используя специальные типы данных - есть и огромный. Но есть он только в массово инстанциируемых в памяти элементах данных. Ваш пример с int stateTable[64][6]; - пример как раз таки правильный, но такие фактические размеры массива слишком малы, чтобы говорить о необходимости экономии. (Однако это зависит и от конкретной платформы, конечно. На каких-нибудь embedded платформах с микроскопическим количеством памяти даже такая экономия может быть существенной.) Если у вас, например, есть структура, создаваемая в миллионах/миллиардах экземпляров, то следя за упаковкой и выравниванием, пользуясь экономными типами данных и битовыми полями, вы во многих случаях сможете в разы уменьшить потребление памяти программой. (Это, кстати, именно то, для чего в языке существуют битовые поля.) Об этом надо беспокоиться заранее и стараться избегать прямой зависимости пользовательских структур данных от явно прописанных типов данных, таких как int, чтобы потом не оказаться в ситуации "надо перелопачивать код всего проекта". Пользуйтесь промежуточными typedef-именами для типов и/или принципом DRY, чтобы избежать прописывания жестко прошитых явных типов в коде программы. Непродуманное прописывание явные типов в коде - это такая же ошибка, как прописывание в коде "магических констант". Разумеется здесь не надо впадать и в другую крайность, т.е. придумывать собственные имена всем типам без исключения. Как правило, ваш опыт и ваше чутье подскажут вам, какие типы в вашей программе являются потенциально "подвижными", т.е. каким типам надо заранее дать пользовательские typedef-имена. Что же касается "оптимизации" размеров локальных переменных, как в вашем примере с for - этого не просто бесполезно, а вредно. "Оптимизировать" единичные локальные переменные смысла нет. Наоборот, в таких ситуациях лучше всегда пользоваться "натуральными" типами - int/unsigned int/size_t для целых значений и double для плавающих, если у вас нет каких-то специальных веских причин использовать другой тип.

воскресенье, 22 декабря 2019 г.

Возможно ли реализовать тип данных более компактный и оптимальный чем String в Java?

#java #строки #типы_данных #типы #теория_типов


Тип данных String (т.е. public final class String) в Java 1.8 занимает 3168 строк
исходного кода (включая все deprecated-методы).
Зачастую, никто не использует все возможности данного типа в конкретном проекте.

Ради спортивного интереса, хочется реализовать компактный тип данных а-ля public
final class CompactFastString с минимальным набором методов и свойств.

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

Но существует мнение, что сложно реализовать более оптимальное решение, чем стандартная
Java SE библиотека.

Что вы думаете на этот счет?
    


Ответы

Ответ 1



Более быстрый в общем случае не получится. Стандартные типы пакета java.lang очень оптимизированы на уровне JVM. Тот исходный код, который вы видите в этих классах - для информации и дебага, а в реальности он заменяется на исключительно быстрые инструкции с учетом возможностей процессора. Например, сравнение строк будет не посимвольным, а с помощью пакетных инструкций SSE, которые умеют сравнивать по 4 байта за такт процессора. В этом и есть преимущество JIT компиляции. P.S. Архитекторы виртуальной машины тоже не очень довольны занимаемой памятью под строки, поэтому планируется в будущих версиях сделать 2 режима работы строк - utf-16 и только latin-1. Но опять же это будет на уровне JVM.

суббота, 21 декабря 2019 г.

Что такое именованный тип данных?

#c_sharp #терминология #типы_данных


В экзаменационном билете есть вопрос : Что такое именованный тип данных. Опишите
как он используется." Я что-то не пойму,это имеются ввиду строки? 
    


Ответы

Ответ 1



Тип, явно объявленный с именем. Например, любой класс или структура, которые вы объявляете, является именованным типом. Пример именованного типа: public struct CoOrds { public int x, y; public CoOrds(int p1, int p2) { x = p1; y = p2; } } Существуют также анонимные типы, объявленные без имени, и компилятор присваивает имя этому типу, недоступное разработчику. Пример анонимного типа: var v = new { Amount = 108, Message = "Hello" }; Вы можете больше узнать о различных типах в C#.