Страницы

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

среда, 5 февраля 2020 г.

Узнать размер колонки

#sql #sql_server #sql_server_2008


Для того, чтобы узнать размер всей бд, или, например, одной таблицы, у нас есть команда
'sp_spaceused'.
Но если пойти дальше, то возникает вопрос: а как получить статистику по полям? Как
узнать, в каком поле (колонке) в таблице содержатся самые тяжёлые данные, а в каком
самые легкие?

Вопрос не столько теоретический, сколько практический: есть огромная база, которую
надо проанализировать (постепенно погружаюсь в Data-Mining).
    


Ответы

Ответ 1



Используйте функцию DATALENGTH() В следующем примере находится длина столбца ProductName в таблице MyOrderTable. SELECT DATALENGTH(ProductName) as [SizeInBytes] FROM MyOrderTable Для справки: DATALENGTH

Ответ 2



Как узнать, в каком поле (колонке) в таблице содержатся самые тяжёлые данные, а в каком самые легкие? Вас интересует размер в байтах, занимаемый тем или иным столбцом на диске (видимо так, раз вы упомянули sp_spaceused)? Не уверен, что возможно определить его точно. Точно можно узнать, сколько страниц (блоков по 8Кб, которыми SqlServer хранит данные) занимают данные всей таблицы (или индекса). Месту на диске, отведенному под хранение конкретного столбца, по-видимому можно дать лишь некоторую оценку (которая, впрочем, не всегда будет адекватной). И с datalength не совсем всё просто. Далее несколько подробнее. Возьмём запрос /*1*/ select sum(datalength([Column])) from [Table]; за основу оценки. Во-первых. Кроме собственно данных столбца всегда есть дополнительная служебная информация, которая может иметь отношение к столбцу, но её объем между столбцами логически может делиться непропорционально их количеству в таблице (например данные заголовка строки). Т.е. оценку размера /*1*/ следует воспринимать как "не менее чем". Чем меньше в таблице столбцов, и чем короче запись, тем больше издержки на служебные данные, тем, соответственно, дальше оценка /*1*/ от реальности. Так, для таблицы с одним коротким столбцом полный размер данных (с учётом служебной информации) может значительно превосходить "логический" размер данных самого столбца. Сравните, например, для таблицы create table tiny_nums (value tinyint); insert into tiny_nums (value) select top (1048576) 1 from master.sys.all_columns a cross join master.sys.all_columns b; результат, возвращаемый запросом /*1*/ с тем, что покажет sp_spaceused. Во-вторых. Значение, возвращаемое datalength не всегда соответствует действительности. В частности, если datalength([Column]) возвращает NULL, то физически это может быть вовсе не ноль. Дело в том, что типы столбцов делятся на fixed-length (напр. int, char(20), datetime2(0), uniqueidentifier, и т.п.) и variable-length (напр. varbinary(64), nvarchar(30) и т.п.). И если для variable-length оценка /*1*/ приблизительно справедлива, то для fixed-length столбцов резервируется место для хранения значения, даже если само значение NULL. Т.е. для fixed-length столбцов оценку /*1*/ следует скорректировать, используя вместо NULL (если они возможны) какое-либо непустое значение, соответствующее типу столбца (например 0 для int): /*2*/ select sum(datalength(isnull([IntColumn], 0))) from [Table]; Также нужно учитывать, что для столбцов типа bit возвращаемое datalength значение равно 1. Однако если в таблице (или индексе) несколько bit столбцов, то SqlServer объединяет их по 8 в 1 байт. Также столбцы могут быть sparse, что означает 0 байт на хранение NULL (даже для fixed-length), но плюс 4 дополнительных байта на хранение значения, если оно не NULL: /*3*/ select sum(datalength([SparseColumn]) + 4) from [TableName]; В-третьих. Если столбец не просто присутствует в таблице, а ещё и участвует в индексах, то он "утяжеляется" кратно количеству индексов, в которых он участвует. Если столбец является ключевым в кластерном индексе, то нужно прибавить оценку кратную количеству всех некластерных индексов (т.к. в leaf-level страницах некластерных индексов содержатся значения ключей кластерного индекса). Так в таблице create table SomeTable ( PK_ID bigint primary key, FK_1_ID int, FK_2_ID int, ... FK_10_ID int, UID uniqueidentifier ); create index IX_1 on SomeTable (FK_1_ID); ... create index IX_10 on SomeTable (FK_10_ID); самым "тяжёлым" скорее всего окажется вовсе не UID столбец, а PK_ID, т.к. (помимо участия в кластерном первичном ключе) значения PK_ID будут присутствовать ещё в 10-ти некластерных индексах. Следует учесть также, что если некластерный индекс является фильтрованным индексом, то соответствующую оценку (/*1*/, /*2*/ или /*3*/) нужно взять не по всей таблице, а по строкам, соответствующим фильтру такого индекса. В-четвертых (относится к Enterprise edition). Если применяется сжатие строк или страниц таблицы alter table [TableName] rebuild partition = all with (data_compression = row); либо индекса alter index [IX_Name] on [TableName] rebuild with (data_compression = page); то оценки с помощью datalength перестают быть адекватными и фактор "не менее чем" перестаёт работать. Сравните для таблиц create table strings (value char(2000)); insert into strings (value) select top (10000) replicate('a', 2000) from master.sys.all_columns a cross join master.sys.all_columns b; и create table strings_page (value char(2000)) with (data_compression = page); insert into strings_page with (tablock) (value) select top (10000) replicate('a', 2000) from master.sys.all_columns a cross join master.sys.all_columns b; значения оценки размера столбца с помощью datalength c тем, что покажет sp_spaceused. Для первой таблицы "показания" datalength и sp_spaceused будут близки (т.к. строка таблицы "широкая" и объем служебной информации сказывается мало), а для второй будут расходиться очень сильно. В-пятых. Всё что было сказано до этого момента справедливо для SqlServer 2008. В более поздних версиях появились COLUMNSTORE индексы, которые, из-за особенностей своего устройства, могут хранить данные в существенно сжатом виде. Для них оценка размера столбца с помощью datalength также может давать неадекватный результат. Если для таблицы create table strings_col (value char(2000)); insert into strings_col (value) select top (10000) replicate('a', 2000) from master.sys.all_columns a cross join master.sys.all_columns b; create clustered columnstore index ix_clcs on strings_col; сравнить показания sp_spaceused с datalength, то опять можно наблюдать сильное расхождение. Полагаю, что данный список факторов, которые следует учитывать при оценке места, занимаемого тем или иным столбцом, не исчерпывающий.

C#,ASP.NET MVC IoC-контейнер Ninject [закрыт]

#c_sharp #aspnet #aspnet_mvc #ninject


        
             
                
                    
                        
                            Закрыт. Данный вопрос необходимо конкретизировать. Ответы
на него в данный момент не принимаются.
                            
                        
                    
                
                            
                                
                
                        
                            
                        
                    
                        
                            Хотите улучшить этот вопрос? Переформулируйте вопрос,
чтобы он был сосредоточен только на одной проблеме, отредактировав его.
                        
                        Закрыт 3 года назад.
                                                                                
           
                
        
Объясните на пальцах про IoC-контейнер Ninject — как используется и в целом зачем
все это надо? 

Читаю литературу, никак не могу понять. Лучше на практике один раз увидеть.
    


Ответы

Ответ 1



На пальцах вряд ли получится, тема сложная. Возникла она приблизительно тогда же, когда объектно-ориентированные языки стали применять для разработки многозвенных приложений. Типичное многозвенное приложение состоит из трёх уровней: уровня представления, уровня предметной области и уровня доступа к данным. В классической схеме зависимость между уровнями распространяется сверху вниз: уровень представления зависит от уровня предметной области, а тот, в свою очередь — от уровня доступа к данным. +----------------------------+ | Уровень представления | +----------------------------+ \/ +----------------------------+ | Уровень предметной области | +----------------------------+ \/ +----------------------------+ | Уровень доступа к данным | +----------------------------+ Рассмотрим простой пример с веб-приложением. Пусть, например, у нас есть список заказов, которые мы хотим показывать пользователю: public OrderController : Controller { [HttpGet] public ActionResult Index() { var userId = GetCurrentUserId(); var repository = new OrderRepository(); var orders = repository.GetAllByUserId(userId); return View(orders); } } Для рендеринга используем простую разметку: @model IEnumerable @{ ViewBag.Title = "Список заказов"; } @foreach (var order in Model) { }
Номер Дата Сумма
@order.Number @order.Date @order.Amount
Это уровень представления, где конечный пользователь видит список заказов на сайте. Чтобы получить этот список, мы обращаемся к хранилищу заказов (это класс уровня предметной области) и получаем заказы (тоже предметная область). Список этих заказов мы отправляем в движок рендеринга HTML, который и превратит их в веб-страницу. А вот как выглядит код уровня доступа к данным, если обращается к данным через Entity Framework: public OrderRepository { public IReadOnlyCollection GetAllByUserId(int userId) { using (var dbContext = new MyDbContext()) { return dbContext.Orders .AsNoTracking() .Where(x => x.UserId == userId) .AsEnumerable() .Select(x => new Order(x.Id, x.Number, x.Date, x.Amount)) .ToArray(); } } } Мы загружаем из БД сырые данные, и создаём из них классы предметной области, обладающие также и поведением. Классы уровня доступа к данным, это, например, MyDbContext и OrderData. Получается вроде бы всё хорошо и расширяемо, но. Что нужно, чтобы перейти в таком приложении от веб интерфейса к оконному? В идеале — написать один новый уровень представления, оконный. Два оставшихся уровня останутся прежними. Здорово. А что нужно, чтобы перейти в таком приложении с SQL на или MongoDb или файловое хранилище? Переписать все уровни, поскольку нижний переписывать всё равно придётся, а вслед за ним придётся переписывать всё, что от него зависит. Нездорово. Что можно сделать, чтобы упростить перенос таких приложений на другие хранилища? Инвертировать зависимость, то есть сделать так, чтобы уровень доступа данных зависел от уровня предметной области. +----------------------------+ | Уровень представления | +----------------------------+ \/ +----------------------------+ | Уровень предметной области | +----------------------------+ /\ +----------------------------+ | Уровень доступа к данным | +----------------------------+ Если мы сделаем так, то получится, что предметная область станет центральной. От неё будут зависеть уровни представления (веб, консоль, десктоп) и уровня доступа к данным (SQL, MongoDb, XML-файлы). Мы сможем расширять приложение, добавляя модули сверху и снизу, поскольку они будут зависеть только от уровня предметной области. Возникает вопрос: но ведь тогда появляется зависимость от центрального уровня предметной области? Что, если мы захотим переписать её? Ответ неожиданный: именно предметная область определяет всё приложение. Если это Word то в предметной области описаны такие штуки, как документы, параграфы, форматирование и всё остальное. В отличие от предыдущих случаев задача подменить предметную область просто бессмысленна, у вас получается другое приложение. Значит, инвертирование зависимости вещь полезная. Но как её осуществить практически? Практически мы должны описать абстракцию (интерфейс) доступа к данным. Вместо конкретного класса OrderRepository у нас появляется интерфейс: public interface IOrderRepository { IReadOnlyCollection GetByUserId(int userId); } Это интерфейс предметной области. Он используется в контролере OrderController нашего MVC приложения (то есть он доступен с уровня представления). public OrderController : Controller { private readonly IOrderRepository _orderRepository; public OrderController(IOrderReposiotory orderRepository) { _orderRepository = orderRepository; } [HttpGet] public ActionResult GetOrders() { var userId = GetCurrentUserId(); var orders = _orderRepository.GetAllByUserId(userId); return View(model); } } Код отличается от предыдущего тем, что уже не может создать объект класса OrderRepository непосредственно, более того, из контроллера этот класс совсем недоступен. Мы имеем доступ только к абстракции (интерфейсу) и ожидаем получить реализацию через конструктор контроллера. Кто-то снаружи должен создать объект OrderRepository и передать его экземпляр в наш конструктор. Пока отложим рассмотрение вопроса, кто этот «кто-то» и посмотрим, как репозиторий реализован на уровне доступа к данным. public OrderRepository : IOrderRepository { public IReadOnlyCollection GetAllByUserId(int userId) { using (var dbContext = new MyDbContext()) { return dbContext.Orders .AsNoTracking() .Where(x => x.UserId == userId) .AsEnumerable() .Select(x => new Order(x.Id, x.Number, x.Amount)) .ToArray(); } } } Здесь интерфейс одного уровня реализуется на другом уровне. Физически зависимость означает, что из проекта, где реализован OrderRepository должен стоять reference на проект, где описан IOrderRepository и в данном случае мы видим, что зависимость инвертирована: уровень доступа к данным зависит от уровня представления. Теперь, если мы захотим изменить представление, нам не надо менять OrderRepository, достаточно вместо веб-интерфейса реализовать другой, например, консольный интерфейс. Если мы захотим изменить реализацию с EF на NHibernate, нам достаточно будет переписать только репозитории, не трогая весь остальной проект, например, контроллеры. Остаётся вопрос: а кто же увязывает друг с другом интерфейсы и реализации? Тот самый IoC-контейнер, в частности, NInject. В проекте, где создается этот контейнер, сходятся все зависимости, поэтому он называется корнем композиции. Что нужно сделать? Нужно подменить стандартный IDependencyResolver из ASP.NET MVC своей реализацией на базе NInject и зарегистрировать в контейнере свои зависимости. О реализации написано, например, здесь. Регистрация выполняется в приватном методе AddBindings: public class NinjectDependecyResolver : IDependencyResolver { private readonly IKernel kernel; public NinjectDependecyResolver() { kernel = new StandardKernel(); AddBindings(); } public object GetService(Type serviceType) { return kernel.TryGet(serviceType); } public IEnumerable GetServices(Type serviceType) { return kernel.GetAll(serviceType); } void AddBindings() { kernel.Bind().To(); . . . } } Далее, в Global.asax меняем объект для разрешения зависимостей: protected void Application_Start() { DependencyResolver.SetResolver(new NinjectDepedencyResolver()); } После этого при попытке создать OrderController движок обратится к NinjectDependencyResolver, который обнаружит, что в конструкторе ожидается объект, реализующий интерфейс IOrderRepository. Движок NInject пройдёт по графу зарегистрированных объектов и выяснит, что этот интерфейс реализует класс OrderRepository который может быть создан непосредственно (то есть у него пустой конструктор). NInject создаст этот класс и передаст ссылку на него в конструктор OrderController. Этот паттерн называется внедрением через конструктор. Созданный контролер будет передан в движок ASP.NET MVC и обработает запрос Index. Помимо NInject существует большое количество IoC-контейнеров: Castle Windsor, Autofac, Unity. При желании, можно даже обрабатывать зависимости вручную, что иногда делают в небольших проектах. Подытожу основные моменты: Мы вводим интерфейсы между уровнем предметной области и уровнями более низких уровней (классически это уровень доступа к данным, в современных приложениях таких может быть больше, чем один). Объекты предметной области и вышележащих уровней для работы используют только интерфейсы, получая их через конструктор. Например, если нам в контролере нужен репозиторий, мы описываем параметр в конструкторе и сохраняем переданное значение в приватной переменной объекта. Мы реализуем все используемые интерфейсы на нижележащих уровнях. Мы сводим воедино вместе все интерфейсы и их реализации (в классической терминологии IoC — регистрируем (register) интерфейсы в контейнере, в терминологии NInject — назначаем (bind) реализацию). Все зарегистрированные объекты образуют граф объектов. Мы используем IoC-контейнер для создания классов верхнего уровня (в MVC приложении это контролеры). Чтобы это сделать, MVC предоставляет интерфейс IDependencyResolver и статический метод DependencyResolver.SetResolver. Контейнер разрешает все промежуточные зависимости, пользуясь графом объектов. Внизу графа находятся примитивные классы с пустыми конструкторами, которые создаются непосредственно. Всё это позволяет прозрачным образом реализовать инверсию зависимостей. Наш код становится проще. Модульные тесты разрабатывать проще. При необходимости, перейти с одной СУБД на другую СУБД или даже совсем не СУБД — такой переход тоже сделать проще.

Ответ 2



На пальцах (простой тупой пример с картинкой и почти без кода). Об ошибках и уточнениях пишите - поправлю. Чтобы лучше понять просто создайте тестовый проект, который будет из файлов разного формата считывать одни и те же данные (csv, json, xml, txt, любой другой по желанию). Или что-то аналогичное. Пусть я хочу написать сайт. Он будет отображать каталог какой-то продукции. Продукции всего 30 предметов и ожидается, что в ближайший год ее станет максимум 33 предмета. Я решил, что 30 предметов можно легко сохранить в JSON файле. Его можно легко редактировать руками и предметов в нем мало - ничего страшного. Создаю новое ASP.Net приложение. В нем будет контроллер CatalogController, отображение ListAllItems, класс ProductItem для одного продукта, класс для считывания файла ProductFileReader и класс для парсинга (он будет создавать список из ProductItem) ProductParser. Картинка: И тут начальник сказал что он хочет тащить список из 1С... . Придется переписать код, который использует ProductParser и ProductFileReader, потому что они уже не подходят. Тут, к счастью, все это используется только в CatalogController. Можно, конечно, все один раз переписать с мыслью, что ничего точно не поменяется (ага, никогда). Здесь приходит мысль, что в контроллере не требуется вызывать все это. Тут нужно получить только список продукции и откуда он возьмется нам все равно. То есть сюда можно ввести зависимость. От чего? Создадим интерфейс IProductProvider и будем в контроллере им пользоваться. Интерфейс (точнее класс который его реализует) даст нам список продукции и не важно как он получился и откуда он взялся - главное, что он есть в нужном месте. О создании нужного объекта для требуемого интерфейса позаботится, например, NInject. И эта зависимость будет только в одном месте (в настройках NInject). private static void RegisterServices(IKernel kernel) { kernel.Bind() .To(); } ProductProviderFromFile будет считывать файл и парсить. Можем потом поменять на ProductProviderFromDB, который получит список из базы. Тогда блок с контроллером будет выглядеть так: Таким образом переписывать контроллер не придется, если мы захотим перейти на хранение в базе. То же самое с модульным тестированием. Нам надо протестировать только контроллер отдельно от всего, а к нему гвоздями прибиты классы для считывания файлов и парсинга и от них легко не избавиться. Тут с помощью NInject или вообще без него можно просто подсунуть контроллеру любой другой класс который реализует интерфейс IProductProvider (он может просто вручную забитый список из трех тестовых продуктов возвращать - какая разница, хоть пустой).

Java script использование нескольких тернарных операторов (специфический случай)

#javascript


В тесте столкнулся с такой ситуацией:

     var y=10;
     var z=3;
     var x=(y%5>0)?(z==3)? z*2 : y+3 :(z<3)? y-z :z++;
     console.log(x); // 3


Подскажите какова последовательность вычислений в такой строке. Спасибо!
    


Ответы

Ответ 1



Если в самом последнем условии прописать ++z, то x будет равен 4

Ответ 2



Двоеточие всегда относится к последнему перед ним вопросительному знаку. Последовательность действий следующая: x= y%5>0 ? ( z==3 ? z*2 : y+3 ) : ( z<3 ? y-z : z++ ) ;

Ответ 3



Здесь вы можете найти подробный разбор выполнения тернарных операторов в различных языках программирования, в том числе и в javascript. Порядок в js простой и логичный: сначала проверяется самое левое условие. Если оно верно, то выполняется и возвращается левая часть после первого двоеточия, если не верно, то правая. Следует отдельно уточнить, что операторы выполняются справа налево. В приведенном вами примере выполнение можно представить примерно такими шагами: (y%5>0)?(z==3)? z*2 : y+3 :(z<3)? y-z :z++; //исходный код false ? true ? z*2 : y+3 : false ? y-z : z++ //выполнили скобки //теперь можно выделить три отдельных тернарных оператора: //(false ? (true ? z*2 : y+3) : (false ? y-z : z++)) //выполняем их справа налево false ? true ? z*2 : y+3 : z++ false ? z*2 : z++ z++ //так как здесь постинкремент - получаем просто "3"

Git GUI - Переключение между ветками с удалением и восстановлением файлов

#git #gui #git_branch


Можно, конечно, и объяснением через командную строку, но желательно через Git GUI
(а иначе зачем создавать gui-клиент, который не может того, что может консоль).

Суть в том, что у разных веток разное количество файлов. И часть из файлов используется
'вручную'. В общем, мне нужно, чтобы при переключении с Ветки1 на Ветку2 удалялись
файлы, не используемые Веткой2 и создавались те, что используются ей. Такое возможно?

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


Ответы

Ответ 1



В общем, мне нужно, чтобы при переключении с Ветки1 на Ветку2 удалялись файлы, не используемые Веткой2 и создавались те, что используются ей. В целом так и происходит. Давайте для начала разберёмся с терминологией, чтобы точно понимать друг друга. Ветка - это всего лишь легковесный указатель на коммит. Она реализована просто как файл, а в файле лежит номер (SHA-1) коммита. Переключаясь на другую ветку вы на самом деле переключаетесь на другой коммит. Файлы и папки не используются веткой или коммитом. Правильнее говорить, что файл (или конкретное состояние файла) принадлежит коммиту или сохранено в коммите. Коммит внутри себя содержит структуру файлов и папок, которые в нём были сохранены, в том числе содержимое файлов. По своей сути это такая "фотография" рабочей области вашего проекта. При переключении на другой коммит происходит перестроение всех файлов и папок на то состояние, которое сохранено в том коммите. Давайте представим, что у нас есть две ветки: master и feature и рассмотрим различные ситуации. Здесь буквы - это разные коммиты. D feature / A--B--C/--E master* Мы находимся в master и переключаемся на feature: git checkout feature Пусть у нас есть некоторый файл. Обозначим его содержимое в коммитах C, D, E, в рабочей области проекта (working tree, wt) и результат checkout'a (или его попытки). x - файл удалён 1, 2, 3 - различные варианты содержимого | C | E | D | wt | Результат --------------------------------------------------------------------- | 1 | 1 | 1 | 1 | 1, без изменений | 1 | 1 | 1 | 2 | 2, несохранённое изменение останется | 1 | 1 | 3 | 1 | 3, терять нечего, содержимое просто меняется | 1 | 2 | 1 | 2 | 1, аналогично | 1 | 2 | 3 | 2 | 3, аналогично. # итог: если последнее состояние файла закоммичено (E == wt), # конфликтов никогда не будет | 1 | 1 | 2 | 3 | Fail, будет потеря несохранённых изменений | 1 | 2 | 2 | 3 | 3, git распознаёт, что изменения в коммитах одинаковы. | 1 | 2 | 3 | 3 | Fail. Текущее состояние файла равно сохранённому в D, # но с точки зрения текущего коммита это потеря несохранённых изменений. | 1 | 2 | 1 | 3 | Fail. Даже если файл менялся только в одном коммите, # нельзя применить изменение "2" -> "3" к содержимому "1" # теперь поудаляем. | C | E | D | wt | Результат --------------------------------------------------------------------- | 1 | x | 1 | x | 1. Файл восстановлен, как раз то что вам нужно. | 1 | 1 | x | 1 | x. В той ветке файл удалён. | 1 | 1 | x | 2 | Fail, потеря изменений. | 1 | 1 | 2 | x | Fail, удаление - это тоже изменение. | 1 | 1 | 1 | x | x, изменение есть, но конфликта нет. # Файл независимо появился в разных ветках | x | 1 | 1 | 1 | 1, всё нормально | x | 1 | 2 | 1 | 2, даже если содержимое разное | x | 1 | 2 | 2 | Fail, снова потеря, хоть содержимое и совпадает. Итого, результат checkout для каждого файла можно описать таким псевдокодом: if (E == wt) { // Если нет несохранённых изменений, берём содержимое из D return D } else if (E == D) { // Если содержимое не менялось (или менялось одинаково), // То можно оставить текущее содержимое файла return wt } else { // Иначе вероятна потеря данных fail() } Что можно делать с несохранёнными изменениями: Закоммитить Сложить в карман. Отменить с помощью git reset --hard -- filename - в одном файле git reset --hard - отменить все изменения версионируемых (отслеживаемых) файлов git clean -f - вообще почистить рабочую область, в том числе удалить неверсионируемые файлы. C -fx удалит ещё и игнорируемые файлы, с -fd захватит директории, а не только файлы. Вариант -fdx - выжигание напалмом всего, что не прибито гвоздями.

Ответ 2



При git checkout %branch_name% состояние отслеживаемых файлов в репозитории меняется в состояние крайнего коммита этого бранча (т.е. лишние файлы скроются, а нужные - добавятся). Модифицированные и новые (не зафиксированные) файлы остаются висеть. Чтобы очистить репозиторий от неотслеживаемых папок и файлов можно также выполнить git clean -dfx.

Ответ 3



если файлы 'вручную' не закомичены и не лежат в индексе в ветке1, то для их сохранения необходимо положить их 'на полку до востребования' командой git stash save --include-untracked "имя полки" После этого переключиться на ветку2 Для просмотра всех стешей надо использовать git stash list Для извлечения файлов нужно выполнить git stash apply "stash@{0}" Нужно использовать нужный индекс Аналогичен переход с ветки2

Когда использовать path в link_to?

#ruby_on_rails


<% @products.each do |product| %>
  <%= link_to 'Show', product %>
  <%= link_to 'Edit', edit_product_path(product) %>
  <%= link_to 'Destroy', product, method: :delete,
                  data: { confirm: 'Are you sure?' } %>
<% end %>


<%= link_to 'Edit', edit_product_path(@product) %> |
<%= link_to 'Back', products_path %>


Почему для show и destroy не используется product_path, а в оcтальных случаях path
используется
    


Ответы

Ответ 1



Если в рельсовый хелпер link_to передать в качестве параметра ресурс (например, объект ActiveRecord) - хелпер сам построит путь до него. Для этого link_to передает параметры в метод url_for, который строит путь по указанным параметрам. Например: <%= link_to 'Show', product %> вернет путь /products/1, где 1 - это id ресурса. А если указать массив вида [:admin, product] - это создаст ссылку на /admin/products/1. А если - массив вида [:admin, :products] - создаст ссылку на /admin/products. Подобным образом вы можете задавать ссылки и в контроллерах, например, redirect_to [:edit, product] В итоге в ваших вьюхах вы можете заменить... <%= link_to 'Edit', edit_product_path(@product) %> | <%= link_to 'Back', products_path %> ...на... <%= link_to 'Edit', [:edit, @product] %> | <%= link_to 'Back', :products %> ...и результат не изменится.

Ответ 2



Маршруты, выдаваемые рельсовым хелпером resources, можно разделить на две группы: Прямые действия (запрос с которым осмысленен с точки зрения семантики HTTP): index: GET коллекции create: POST ресурса show: GET ресурса update: PATCH ресурса (или PUT, хотя правильность семантики Rails тут под вопросом) destroy: DELETE ресурса ...и вспомогательные заглушки, которые нужны исключительно потому, что "надо рассказать браузеру, как совершить действие": new: интерфейс к create edit: интерфейс к update Прямые действия используют просто путь к тому, над чем производится действие, а само действие опознаётся из HTTP-метода. Но для "заглушек" осмысленных HTTP-методов нет, поэтому они используют пути "с хвостиком". А поскольку хелпер возвращает только путь (но не метод!), для прямых действий путь можно собрать прямо их объекта ресурса, а для заглушек нужен хелпер или объект с "подсказкой". Поскольку форма "подсказок" у маршрутной системы малоизвестна и имеет очень неочевидный синтаксис, её часто избегают в пользу хелперов, но это вопрос стиля.

Ответ 3



Вообще хэлпер link_to принимает путь, который можно получить при помощи path-хэлперов. Однако в том случае, когда редактируется или удаляется один объект ActionPack может автоматически сформировать путь. Ничто не мешает вам использовать хэлепер и в этом случае <%= link_to 'Show', product_path(product) %> Однако так получается длиннее.

Проход по алфавиту Python

#python


Можно ли как-то инкрементировать символ латинского алфавита? Нужно пройтись по алфавиту
в Python, либо по числу получать какой-то определенный символ, есть такие функции?
    


Ответы

Ответ 1



Получение символа по его номеру из Unicode: >>> chr(65) 'A' >>> chr(122) 'z' >>> print(chr(128522)) 😊 (в Python 2 лучше вместо chr использовать unichr) Получение номера из Unicode по символу: >>> ord('A') 65 >>> ord('z') 122 >>> ord('😊') 128522 Заранее заготовленные строки с латинскими (и не только) символами есть в модуле string: >>> import string >>> string.ascii_uppercase 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' >>> string.ascii_lowercase 'abcdefghijklmnopqrstuvwxyz' >>> string.ascii_letters 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' По этим строкам можно проходиться циклом: for c in string.ascii_letters: print(c) Во всяких генераторных выражениях и условиях, разумеется, тоже: >>> import string >>> s = 'fooУБРАТЬbarОТСЮДАbazНЕЛАТИНСКОЕ' >>> s2 = ''.join(c for c in s if c in string.ascii_letters) >>> print(s2) foobarbaz

HTML тег <s> осуждается или его можно использовать?

#html #css #html5 #css3 #вёрстка


HTML тег  осуждается, или его можно использовать? И где вообще самая достоверная
информация?

Или лучше вообще использовать стили вместо тегов, где это можно?
    


Ответы

Ответ 1



Использование спецификацией не осуждается. Это вполне себе стандартный тег, который можно, и нужно использовать. Самая достоверная информация - в спецификации-рекомендации, опубликованной на W3C и в их wiki. Остальное - порой отсебятина авторов. А вот , кстати говоря, как раз устарел и был исключен из стандарта - вместо него теперь нужно использовать или (в зависимости от ситуации)

Какой смысл в числах SYN и ACK в протоколе TCP?

#tcp_ip #tcp


Привет. Не понимаю, что значит SYN и ACK при установке соединения по протоколу TCP.
Кучу видео пересмотрел и не понял. Кто разобрался в этом?  Какой смысл в этих SYN и
ACK (зачем их вообще придумали)? Какими-то непонятными числами компьютеры обмениваются,
какие-то приращения SYN на единицу... Нужен какой-то просто пример, понятный для того,
кто вообще в сетях не разбирается.
    


Ответы

Ответ 1



Придумали их с той важной целью, что пакеты, передющиеся по TCP, могут прийти не в той же последовательности что были отправлены и не в том же составе. Нужен механизм, который позволит собрать набор полученных пакетов в правильной последовательности. И заодно проверить все ли пакеты присутствуют или кто-то на пол пути сошёл с дистанции и потерялся. Эту задачу и решают при помощи номеров очереди и номеров подтверждений. Номера очереди (номера последовательности) - просто нумеруют отправляемые пакеты. Это число увеличивается в зависимости от длины поля данных. Каждый октет данных (т. е. каждый байт) одного пакета имеет свой номер очереди. Номер очереди первого октета данных и передаётся в заголовке TCP пакета, он же и считается номером очереди для пакета. Номера подтверждений - сообщают другой стороне номер очереди который ожидается получить от неё следующим. Они говорят, что пакеты со всеми предыдущими номерами очередей (но не включая этот) уже были получены. Первоначальный номер очереди высылается клиентом при установлении соединения вместе с флагом SYN. Сервер в ответ высылает номер подтверждения (полученный номер очереди + 1) и свой номер очереди (в целом любой, но при использовании механизма SYN coockie построенному по определённому алгоритму). Сервер в данный момент сообщает клиенту что ожидает от него пакет, который будет иметь номер очереди равный отправленному номеру подтверждения. От этого номера клиент в дальнейшем и отталкивается. Далее всё происходит таким образом - одна сторона (сторона А) отправляет другой (стороне Б) пакеты, пронумерованные номерами очередей. Вторая сторона принимает их и сообщает номер очереди, которая она ожидает получить от А со следующим пакетом. Это говорит о том, что сторона Б получила все пакеты, у которых номер очереди был ниже переданного номера подтверждения (но не равен ему) и что сторона Б ожидает, что в следующей партии переданных данных нумерация начнётся как раз с этого номера. На всякий случай ещё раз - поле TCP Номер очереди (Порядковый номер) означает просто номер пакета, нужен для того, что бы пакеты правильно собрать и обнаружить пропажу (или дубликат). Поле Номер подтверждения служит для информирования второй стороны о том какие пакеты были от неё уже получены (с какими порядковыми номерами) и содержит число, которое ожидается увидеть в поле Номер очереди следующего полученного пакета от этого же источника. P. S. SYN и ACK это всё же флаги, а не числа. Они говорят о том, что задействованы соответствующие поля заголовка (флаги TCP)

При передаче массива строк в функцию строки равны NULL

#c


Есть непонятная проблема по передача массива строк в функцию на языке Си.

Вызывающий код:

char str[33][40];
CreateCSV((char**)str);


Функция:

void CreateCSV(char** str)
{
    char strData[]= {'"','Д','А','Т','А','"',';','\0'};

    if(str[0x00] != NULL)
        strcpy (str[0x00], strData);
    else
        Console("СтрокаЗаголовка[0x00] == NULL");
}


Почему у меня str[0x00] == NULL? Ведь я явно создал 33 строки и зарезервировал по
40 символов под каждую.
    


Ответы

Ответ 1



Если в кратце - используйте одинаковый прототип для объявления переменной и для параметров в функции и для strData. Это не самый красивый вариант, но он сработает Вот как будет выглядеть Ваша программа: #include #include void CreateCSV(char str[33][40]) { char strData[33][40]= {"\"","D","A","T","A","\"",";","\0"}; if(str != NULL) memcpy (str, strData, sizeof(strData)); else printf ("error\n"); } int main (void) { char str[33][40] = {'\0'}; int i=0; CreateCSV(str); for (i=0; i<33; i++) printf ("%s\n", str[i]); return 0; } Обратите внимание на сл. нюансы - я использовал функцию копирования памяти а не строк "memcpy". И проверял на NULL имя str без указания сдвига. Это связано с тем, как язык Си представляет и обрабатывает многомерные массивы. Также при заполнении strData использовал двойные кавычки, иначе компилятор соединял буквы в одну строку, а не трактовал их как ряд строк с 1 буквой в каждой строчке. Если в деталях и особенностях физики языка Си: Массивы объявленные как char str[33][40]; и как char str**; это совершенно разные вещи. char str[33][40]; - трактуеться как длинный одномерный массив глде все строки слеплены в одну одна-за-другой. Что-то ввиде "строка 1 \0\0\0\0\0...\0 строка 2 \0\0\0\0\0...\0 ....... строка 33 \0\0\0\0\0...\0 " и размер такого массива будет равер 33*40*sizeof(char)=33*40*1=1320. При объявлении такого массива, компилятор выделяет 1320 байт в стеке, и трактует переменную 'str' как указатель на эту длинную строку. char str;** - это массив указателей на одномерные строки. Воспринимаеться компилятором как {*pointer_to_str1, *pointer_to_str2, ... *pointer_to_str33}, при этом сами строки лежат вне этого массива. Размер такого массива будет равен 33*sizeof(char*) = 33*4 = 132/ При объявлении такого массива, компилятор проводит несколько операций. Сначала компилятор выделит 33 независимых строки в стеке , затем создаст массив указателей и заполняет его указателями на эти 33 независимые строки. Вот как будет выглядеть Ваша программа если strData объявить как char**: #include #include void CreateCSV(char str[33][40]) { char *strData[33] = {"\"","D","A","T","A","\"",";","\0"}; if(str != NULL) { int i=0; for (i=0; i<33; i++) if (strData[i] != NULL) strncpy (str[i], strData[i], 40); } else printf ("error\n"); } int main (void) { char str[33][40] = {'\0'}; int i=0; CreateCSV(str); for (i=0; i<33; i++) printf ("%s\n", str[i]); return 0; } PS оба примера проверены на gcc

Ответ 2



Основная проблема - в преобразовании (char**)str тип str - char (*)[33]. Рассмотрим простую программу: int main() { long a[10][10]; for (int i=0;i<10;i++) for (int j=0;j<10;j++) a[i][j] = -10000 - i*100 - j; cout << (long)a< *(a + i*10 + j). В массиве int ** данные упакованы не подряд а (примерно) так: b --> [0][1]...[9] b[0] --> [0][1]...[9] b[1] --> [0][1]...[9] ... b[9] --> [0][1]...[9] При этом они даже не обязаны лежать подряд. b[i][j] <==> * ( *(b+i) + j) Обратите внимание, что нужно уже 2 раза разыменовывать указатель. В этом смысле массивы a[10][10] можно считать одномерными, а остальное - удобство пользователя. После преобразования происходит следующее (доступ в b[3][5] к примеру): (адрес a и b совпадает) берётся элемент по адресу (b + 3) смотрим в массив a --> это будет элемент a[3] берётся элемент по адресу ( a[3] + 5) в a[3] какое-то значение, мы его пытаемся разыменовать - UB (а обычно SEG_FAULT) Кстати компилятор не просто так запрещает прямое присваивание int **b = a; Имеет смысл читать эти предупреждения. Если вам нужно передать такой массив в функцию, то есть разные варианты, как правильно написать прототип func( char x[33][40]) первый размер можно не писать func( char x[][40]) - самые простые.

Сравнение скорости загрузки серверного шрифта и google-шрифта

#html #css #вёрстка #шрифты #google_fonts


На англоязычном стаке задавал вопрос о проблемах подключения шрифта через @font-face.

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

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


Ответы

Ответ 1



Для начала, стандартные плюсы и минусы CDN: + Используется многими сайтами, поэтому весьма вероятно, что будет взято из кэша браузера, что позволит сэкономить на количестве запросов к серверу и объёме загружаемых данных. + Снижает нагрузку на сервер, поскольку ресурс теперь отдаётся не им, а сторонним (в данном случае гугловским) сервером. + Позволяет увеличить число потоков одновременной загрузки. Браузеры используют ограничение - не более 8 потоков загрузки на один домен. Но CDN принадлежит другому домену, поэтому получается 8 потоков на твой сайт и ещё по 8 на каждый чужой домен. Минусы, в основном, сводятся к зависимости от стороннего ресурса: − Если он по какой-то причине недоступен, то у тебя на сайте шрифта не будет. Для скриптов часто делается фоллбек. − Если CDN вдруг начнёт отдавать что-то другое, пользователи увидят результат раньше тебя. Для собственного сервера ведь ты сначала проверишь, что изменилось и только потом изменённые файлы окажутся у пользователей. Пример такой проблемы: Как загрузить шрифт Roboto v15?. ? Теперь есть два сайта, которые можно пытаться взломать. Впрочем, не думаю. что это актуально для шрифтов, да и у гугловских серверов должна быть надёжная защита. Но в случае подмены скрипта все сайты, использующие его, окажутся под угрозой. Хотя, от этого в некоторой мере помогает перманентное кэширование - клиенты, у которых файл уже есть в кэше, на сервер за ним даже не пойдут. − Если свой сервер находится в локальной/корпоративной сети, то скорость доступа к нему выше, чем к CDN. Кроме того, с ним можно работать даже если интернет недоступен. Теперь что касается именно шрифтов: ? Гуглошрифты позволяют указать набор интересующих тебя символов, что позволит сократить объём файлов шрифта. С другой стороны, чем более хитрый набор ты укажешь, тем меньше вероятность, что нужный файл уже будет в кэше браузера. + Скорее всего, гуглошрифты уже оптимизированы под использование на веб-сайтах. Не могу сказать, как именно, но на 90% уверен, что если это возможно, то они об этом позаботились.

Ответ 2



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

Почему argv[0] выдаёт полный путь к скрипту? (Python)

#python #argv


Выполняю упражнения из LearnPythonTheHardWay. Везде, где используется argv, у автора
первый аргумент содержит имя файла скрипта (например, "ex14.py"), у меня же выдаётся
полный путь к этому файлу - "D:_MyCode\Python\LPTHW\ex14.py" (запускаю из cmd). От
чего это зависит? Что и где нужно изменить, чтобы argv[0] содержала просто имя скрипта?
    


Ответы

Ответ 1



От чего это зависит? Документация для python