У меня есть строка, которую я хотел бы перепроверить, если она состоит исключительно из букв и пробелов.

 val str = "my long string to test"
 val purealpha = " abcdefghijklmnopqrstuvwxyz".toSet

 if (str.forall(purestring(_))) println("PURE") else "NOTPURE"

Вышеуказанный код CONCISE делает свою работу. Однако, если я запусту это так:

 val str = "my long string to test"
 val purealpha = " abcdefghijklmnopqrstuvwxyz"   // not converted toSet

 str.forall(purealpha(_))    // CONCISE code

Я получаю сообщение об ошибке (найдено: Char ... required: Boolean), и оно может работать только с использованием метода contains следующим образом:

str.forall(purealpha.contains(_))

Мой вопрос заключается в том, как я могу использовать форму CONCISE без преобразования строки в набор. Любые предложения о том, чтобы иметь свой собственный класс String с правильной комбинацией методов, чтобы включить хороший код; или, может быть, некоторые чистые функции, работающие со строками.

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

1
user-asterix 27 Май 2017 в 23:18

2 ответа

Лучший ответ

Немного другой подход заключается в использовании шаблона регулярных выражений.

val str = "my long string to test"
val purealpha = "[ a-z]+"
str matches purealpha   // res0: Boolean = true
1
jwvh 28 Май 2017 в 00:13

Если мы посмотрим на исходный код, то увидим, что обе эти реализации делают разные вещи, хотя дают одинаковый результат. Когда вы конвертируете его в Set и используете forAll, вы в конечном итоге вызываете метод apply для набора. Вот как явно вызывается apply в вашем коде, также используя именованные параметры в анонимных функциях:

if (str.forall(s => purestring.apply(s))) println("PURE") else "NOTPURE" // first example
str.forall(s => purealpha.apply(s)) // second example

В любом случае, давайте посмотрим на исходный код для подачи заявки на Set (полученный из GenSetLike.scala):

/** Tests if some element is contained in this set.
   *
   *  This method is equivalent to `contains`. It allows sets to be interpreted as predicates.
   *  @param elem the element to test for membership.
   *  @return  `true` if `elem` is contained in this set, `false` otherwise.
   */
  def apply(elem: A): Boolean = this contains elem

Когда вы оставляете литерал String, вы должны специально вызывать .contains (это исходный код, полученный из SeqLike.scala):

/** Tests whether this $coll contains a given value as an element.
   *  $mayNotTerminateInf
   *
   *  @param elem  the element to test.
   *  @return     `true` if this $coll has an element that is equal (as
   *              determined by `==`) to `elem`, `false` otherwise.
   */
  def contains[A1 >: A](elem: A1): Boolean = exists (_ == elem)

Как вы можете себе представить, подача заявки на литерал String не даст того же результата, что и подача заявки на Set.

Предложение о большей краткости состоит в том, чтобы полностью опустить (_) во втором примере (вывод типа компилятора подхватит это):

val str = "my long string to test"
val purealpha = " abcdefghijklmnopqrstuvwxyz"   // not converted toSet
str.forall(purealpha.contains)
1
Tanjin 27 Май 2017 в 21:00