Вопрос или проблема
Я пытаюсь написать пользовательскую функцию Power Query, чтобы преобразовать данную строку в список ее биграмм.
Например, “Привет, мир” должен вывести
Список | |
---|---|
1 | Пр |
2 | ри |
3 | ив |
4 | ве |
5 | ет |
6 | , |
7 | м |
8 | и |
9 | р |
Я наткнулся на реализацию рекурсивной функции на M здесь, что привело меня к написанию этого:
( String1 as text ) =>
let
S1 = Text.Remove(String1, " "),
String1_Tokens = Text.ToList(S1),
N = List.Count(String1_Tokens),
func = (x as list, n as number, y as list) =>
let
Calc = Text.Combine({x(n), x(n+1)}),
Lister = List.Combine({y,{Calc}}),
Check = if n = N-1 then Lister else @func(x, n+1, Lister)
in Check,
res = func(String1_Tokens, 1, {})
in res
Основная идея заключается в том, что строка очищается от пробелов и разбивается на список ее составных символов. Этот список затем передается в функцию, которая берет nй и n+1й символ в списке и объединяет их, затем добавляет к уже существующему списку биграмм. Это повторяется, пока список символов не исчерпан.
При попытке вызвать эту функцию с входной строкой я получаю следующую ошибку:
Произошла ошибка в запросе ‘ ‘. Expression.Error: Мы не можем преобразовать значение типа List в тип Function.
Детали:
Значение=[List]
Тип=[Type]
Что я упускаю? Я довольно нов в M, поэтому чувствую, что здесь мне явно не хватает чего-то основного.
Вы почти сделали это!
( String1 as text ) =>
let
S1 = Text.Remove(String1, " "), // Удалить пробелы
String1_Tokens = Text.ToList(S1), // Преобразовать строку в список символов
N = List.Count(String1_Tokens), // Получить длину списка
func = (x as list, n as number, y as list) =>
let
// Объединить текущий и следующий символы
Calc = Text.Combine({x{n}, x{n+1}}),
// Добавить результат в список
Lister = List.Combine({y, {Calc}}),
// Проверить, если мы на предпоследнем элементе, остановить рекурсию, если это так
Check = if n = N-2 then Lister else @func(x, n+1, Lister)
in
Check,
// Начать рекурсию с индекса 0
res = func(String1_Tokens, 0, {})
in
res
Дополнительная заметка: я думаю, вы написали очень элегантную рекурсивную функцию, но если бы я делал это (не то чтобы я был экспертом, это просто то, что мне нравится делать), я бы избегал использования рекурсии (в основном потому, что я всегда теряюсь в ней):
( String1 as text ) =>
let
S1 = Text.Remove(String1, " "), // Удалить пробелы
charList = Text.ToList(S1), // Преобразовать строку в список символов
N = List.Count(charList), // Получить количество в списке
positions = List.Numbers(0, N-1), // Создать список позиций от 0 до N-1
bigrams = List.Transform(positions, each if _ < N-1 then Text.Combine({charList{_}, charList{_+1}}) else null),
// Удалить любые значения null (последний элемент может быть null из-за условия if)
cleanedBigrams = List.RemoveNulls(bigrams)
in
cleanedBigrams
Ответ или решение
Как получить биграмы строки в Power Query M
Вопрос, который вы подняли, касается извлечения биграмм из строки в Power Query M, и ваша реализация действительно близка к правильному решению. Ниже я предложу подробное объяснение задачи и исправления, которые помогут вам достичь желаемого результата.
Что такое биграммы?
Биграмма представляет собой последовательность из двух символов, которые идут один за другим. Например, в строке "Hi World" биграммы будут: "Hi", "iW", "Wo", "or", "rl" и "ld".
1. Анализ Вашего Кода
Ваше решение основано на рекурсивной функции, что, безусловно, является хорошим способом решения задачи. Однако в вашем коде есть несколько моментов, которые требуют уточнения:
- Ошибка, о которой вы сообщаете, возникает из-за неправильного обращения с индексами и, возможно, ошибочного понимания об использовании
List.Combine
. - Вместо использования
n = N-1
, вам следует проверятьn = N-2
, чтобы не выходить за пределы списка.
2. Исправленный Код
Вот исправленная версия вашей рекурсивной функции:
(String1 as text) =>
let
S1 = Text.Remove(String1, " "), // Убираем пробелы
String1_Tokens = Text.ToList(S1), // Преобразуем строку в список символов
N = List.Count(String1_Tokens), // Получаем длину списка
func = (x as list, n as number, y as list) =>
let
// Объединяем текущий и следующий символы
Calc = Text.Combine({x{n}, x{n+1}}),
// Добавляем результат в список
Lister = List.Combine({y, {Calc}}),
// Проверяем, находимся ли мы на предпоследнем элементе
Check = if n = N-2 then Lister else @func(x, n+1, Lister)
in
Check,
// Запускаем рекурсию с первого индекса
res = func(String1_Tokens, 0, {})
in
res
3. Альтернативный Итеративный Метод
Если рекурсия кажется сложной, других разработчиков может привлечь итеративный подход. Вот пример того, как это можно реализовать без рекурсии:
(String1 as text) =>
let
S1 = Text.Remove(String1, " "), // Убираем пробелы
charList = Text.ToList(S1), // Преобразуем строку в список символов
N = List.Count(charList), // Получаем количество символов
positions = List.Numbers(0, N-1), // Создаем список индексов
bigrams = List.Transform(positions, each if _ < N-1 then Text.Combine({charList{_}, charList{_+1}}) else null),
cleanedBigrams = List.RemoveNulls(bigrams) // Убираем возможные null-значения
in
cleanedBigrams
4. Заключение
Теперь у вас есть два подхода для извлечения биграмм из строки в Power Query M. Рекурсивный подход и итеративный метод оба являются действительными, и выбор между ними зависит от ваших предпочтений и требований производительности.
Эти методы обеспечивают корректный способ генерации биграмм, подходя к задаче с разных сторон. Не забудьте протестировать код с различными входными строками для проверки корректности работы.
Если у вас останутся дополнительные вопросы или возникнут трудности в процессе выполнения, не стесняйтесь обращаться за помощью!