Published on

November 25, 2018

Изучение концепций SQL Server: Алхимическое сокращение

Добро пожаловать на еще одну статью в нашей серии о концепциях и идеях SQL Server. Сегодня мы будем обсуждать концепцию алхимического сокращения с использованием SQL Server. Эта тема вдохновлена заданием Advent of Code, где участники решают программные головоломки с использованием различных языков программирования, включая T-SQL.

В задании нам предоставляется полимер, который представляет собой строку, состоящую из единиц, представленных заглавными и строчными буквами. Соседние единицы с одной и той же буквой, но разной полярностью (регистром), уничтожают друг друга. Цель состоит в том, чтобы сократить полимер до его наименьшего возможного размера.

Для достижения этой цели нам необходимо выполнить регистрозависимый поиск и замену для каждой буквы алфавита. Мы можем сделать это с помощью цикла while в SQL Server. Хотя в SQL Server обычно предпочитается множественная рекурсия, она плохо работает в этом сценарии, поэтому цикл while является более подходящим выбором.

Давайте посмотрим на код для выполнения рекурсивных замен:


DECLARE @data    VARCHAR(MAX),
        @Letters CHAR(26)= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
        @Size    INTEGER,
        @NewSize INTEGER = 0,
        @Loop    INTEGER = 0;

SELECT  @data = FileData
FROM OPENROWSET(BULK 'D:AdventOfCode2018InputDay05.txt', SINGLE_CLOB) dt(FileData);

SET @Size = LEN(@data);

WHILE @Size <> @NewSize
BEGIN
    SET @NewSize = @Size;

    SELECT  @data = REPLACE(@data COLLATE SQL_Latin1_General_CP1_CS_AS, ca.UpperLtr + ca.LowerLtr, '')
    FROM (VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13),(14),(15),(16),(17),(18),(19),(20),(21),(22),(23),(24),(25),(26)) dt(N)
    CROSS APPLY (VALUES (UPPER(SUBSTRING(@Letters, dt.N, 1)), LOWER(SUBSTRING(@Letters, dt.N, 1)))) ca(UpperLtr, LowerLtr)
    
    SELECT  @data = REPLACE(@data COLLATE SQL_Latin1_General_CP1_CS_AS, ca.LowerLtr + ca.UpperLtr, '')
    FROM (VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13),(14),(15),(16),(17),(18),(19),(20),(21),(22),(23),(24),(25),(26)) dt(N)
    CROSS APPLY (VALUES (UPPER(SUBSTRING(@Letters, dt.N, 1)), LOWER(SUBSTRING(@Letters, dt.N, 1)))) ca(UpperLtr, LowerLtr)
    
    SET @Size = LEN(@data);
    SET @Loop += 1;
END

SELECT @Loop AS Loops, @Size AS Size;

В этом коде мы сначала объявляем и инициализируем необходимые переменные. Затем мы загружаем полимер из файла в переменную. С помощью цикла while мы выполняем регистрозависимые замены, пока размер полимера не перестанет изменяться. Наконец, мы возвращаем количество выполненных циклов и конечный размер полимера.

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

Вот код для второй части:


DECLARE @data    VARCHAR(MAX),
        @Letters CHAR(26)= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
        @Size    INTEGER,
        @NewSize INTEGER = 0,
        @Loop    INTEGER = 0,
        @Letter  CHAR(1) = 'A',
        @data2   VARCHAR(MAX);

SELECT  @data2 = FileData
FROM OPENROWSET(BULK 'D:AdventOfCode2018InputDay05.txt', SINGLE_CLOB) dt(FileData);

DECLARE @Results TABLE (Letter CHAR(1) PRIMARY KEY, Size INTEGER);

WHILE ASCII(@Letter) <= ASCII('Z')
BEGIN
    SET @data = REPLACE(@data2, @Letter, '');
    SET @NewSize = 0;
    SET @Size = LEN(@data);
    SET @Loop = 0;
    
    WHILE @Size <> @NewSize
    BEGIN
        SET @NewSize = @Size;
        
        SELECT  @data = REPLACE(@data COLLATE SQL_Latin1_General_CP1_CS_AS, ca.UpperLtr + ca.LowerLtr, '')
        FROM (VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13),(14),(15),(16),(17),(18),(19),(20),(21),(22),(23),(24),(25),(26)) dt(N)
        CROSS APPLY (VALUES (UPPER(SUBSTRING(@Letters, dt.N, 1)), LOWER(SUBSTRING(@Letters, dt.N, 1)))) ca(UpperLtr, LowerLtr)
        
        SELECT  @data = REPLACE(@data COLLATE SQL_Latin1_General_CP1_CS_AS, ca.LowerLtr + ca.UpperLtr, '')
        FROM (VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13),(14),(15),(16),(17),(18),(19),(20),(21),(22),(23),(24),(25),(26)) dt(N)
        CROSS APPLY (VALUES (UPPER(SUBSTRING(@Letters, dt.N, 1)), LOWER(SUBSTRING(@Letters, dt.N, 1)))) ca(UpperLtr, LowerLtr)
        
        SET @Size = LEN(@data);
        SET @Loop += 1;
    END
    
    INSERT INTO @Results (Letter, Size)
    VALUES (@Letter, @Size);
    
    SET @Letter = CHAR(ASCII(@Letter)+1);
END

SELECT TOP (1) *
FROM @Results
ORDER BY Size;

В заключение, мы исследовали концепцию алхимического сокращения с использованием SQL Server. Мы узнали, как загружать файл, выполнять регистрозависимые и регистронезависимые операции со строками, использовать конструктор таблицы значений и реализовывать циклы while в SQL Server. Эти концепции ценны для решения различных программных задач и могут улучшить ваши навыки работы с SQL Server.

Спасибо за чтение этой статьи. Следите за новыми статьями о концепциях и идеях SQL Server.

Автор: Ваше имя

Click to rate this post!
[Total: 0 Average: 0]

Let's work together

Send us a message or book free introductory meeting with us using button below.