Я не должен использовать какие-либо встроенные функции, такие как String.sub
Как жаль! Поскольку строки имеют абстрактный интерфейс, а у вас есть списки прямого доступа к его основным конструкторам, []
и ::
, у вас есть для использования библиотечных функций для получения в любом месте со строками. explode
также является библиотечной функцией. Но хорошо, если вы ограничены тем, что вам нужно преобразовать строку в список для решения упражнения, пусть будет так.
Учитывая ваш текущий код,
fun subString(s1,s2) =
if null s2 then ~1
else if s1 = s2 then 0
else 1+subString(s1, tl s2);
Я чувствую одну проблему здесь:
subString ([#"b",#"c"], [#"a",#"b",#"c",#"d"])
~> if null ([#"a",#"b",#"c",#"d"]) then ... else
if [#"b",#"c"] = [#"a",#"b",#"c",#"d"] then ... else
1 + subString([#"b",#"c"], [#"b",#"c",#"d"])
~> 1 + subString([#"b",#"c"], [#"b",#"c",#"d"])
~> 1 + if null ([#"b",#"c",#"d"]) then ... else
if [#"b",#"c"] = [#"b",#"c",#"d"] then ... else
1 + subString([#"b",#"c"], [#"c",#"d"])
Кажется, что проверки s1 = s2
недостаточно, мы должны были бы сказать, что [#"b",#"c"]
является подстрокой [#"b",#"c",#"d"]
, потому что это ее префикс, а не потому, что она эквивалентна. С s1 = s2
вы в конечном итоге проверяете, что что-то является действительным суффиксом , а не действительной подстрокой . Поэтому вам нужно изменить s1 = s2
на что-то умнее.
Возможно, вы можете создать вспомогательную функцию, которая определяет, является ли один список префиксом другого, и использовать это здесь?
Что касается решения этого упражнения путем explode
встраивания ваших строк в списки: это крайне неэффективно, настолько, что родственный язык Standard ML Ocaml explode
полностью удалил из библиотеки:
Функции explode
и implode
были в более старых версиях Caml, но мы исключили их из OCaml, потому что они поддерживают неэффективный код. Как правило, плохая идея рассматривать строку как список символов, и рассматривать ее как массив символов гораздо лучше подходит для реальной реализации.
Итак, во-первых, String.isSubstring
уже существует , так что это решенная проблема. Но если это не так, и кто-то хотел написать это композиционно, и String.sub
не обманывает (он обращается к символу в строке, сравнимо с шаблоном, совпадающим с заголовком и хвостом списка через x::xs
), затем позвольте мне побудить вас написать эффективный, компонуемый и функциональный код:
(* Check that a predicate holds for all (c, i) of s, where
* s is a string, c is every character in that string, and
* i is the position of c in s. *)
fun alli s p =
let val stop = String.size s
fun go i = i = stop orelse p (String.sub (s, i), i) andalso go (i + 1)
in go 0 end
(* needle is a prefix of haystack from the start'th index *)
fun isPrefixFrom (needle, haystack, start) =
String.size needle + start <= String.size haystack andalso
alli needle (fn (c, i) => String.sub (haystack, i + start) = c)
(* needle is a prefix of haystack if it is from the 0th index *)
fun isPrefix (needle, haystack) =
isPrefixFrom (needle, haystack, 0)
(* needle is a substring of haystack if is a prefix from any index *)
fun isSubstring (needle, haystack) =
let fun go i =
String.size needle + i <= String.size haystack andalso
(isPrefixFrom (needle, haystack, i) orelse go (i + 1))
in go 0 end
Общая идея, которую вы можете использовать при построении isSubstring
, использующего рекурсию списка, а не рекурсию строкового индекса, состоит в абстрактном построении алгоритма: можно определить needle
как подстроку haystack
Проще говоря, needle
является префиксом haystack
, считая от любой действительной позиции в haystack
(конечно, не так, чтобы она превышала haystack
). И определить, является ли что-то префиксом, намного проще, еще проще с рекурсией списка!
Это предложение оставило бы вас с шаблоном,
fun isPrefix ([], _) = ...
| isPrefix (_, []) = ...
| isPrefix (x::xs, y::ys) = ...
fun isSubstring ([], _) = ...
| isSubstring (xs, ys) = ... isPrefix ... orelse ...
Что касается оптимизации рекурсивного решения с индексом строки, вы можете избежать проверки двойных границ как в isPrefixFrom
, так и в isSubstring
, сделав isPrefixFrom
локальной функцией, доступной только для isPrefix
и isSubstring
; в противном случае это будет небезопасно.
Проверка этого,
- isSubstring ("bc", "bc");
> val it = true : bool
- isSubstring ("bc", "bcd");
> val it = true : bool
- isSubstring ("bc", "abc");
> val it = true : bool
- isSubstring ("bc", "abcd");
> val it = true : bool
- isSubstring ("bc", "");
> val it = false : bool