HPUNIX Сайт о ОС и не только!

Работа с постоянными выражениями в Scala

7 мая 2012 - unix
Работа с регулярными выражениями в Scala
Работа с регулярными выражениями в Scala

Не так давно мне захотелось прикрутить к блогу виджет, содержащий перечень самых просматриваемых заметок. Так как WordPress не собирает подобающую статистику, количество просмотров предполагалось брать из LiveInternet.

В общем, рядовая ситуация из серии «скачать и пропарсить», берем Perl и вперед. Но ведь мы уже много раз так делали, не любопытно! Давайте лучше поглядим, как управится с этой задачей Scala.

Спустя какое-то время, у меня вышла такая программка:

package me.eax.mostviewed

import scala.io._
import scala.util.matching.Regex._

object Application extends App {
  def buildStatUrl(domain: String, date: String) = {
    s"http://www.liveinternet.ru/stat/${domain}/" +
     s"pages.html?date=${date}&period=month&total=yes&per_page=100"
  }

  def fetchStatUrl(domain: String, date: String) = {
    val url = buildStatUrl(domain, date)
    Source.fromURL(url).mkString
  }

  def getStat(domain: String, date: String) = {
    val data = fetchStatUrl(domain, date)
    val re = """(?s)for="id_\d+"><a href="([^"]+)"[^>]*>.*?<td>([\d,]+)</td>""".r
    for (re(url, count) <- re findAllMatchIn data)
      yield (url, count.split("").filter(_ != ",").mkString.toInt)
  }

  def getTitle(url: String) = {
Работа с регулярными выражениями в Scala
    val data = Source.fromURL(url).mkString
    val reStr = """<h2>(.*?)</h2>"""
    reStr.r findFirstMatchIn data match {
      case Some(x: Match) => x group 1
      case None => throw new RuntimeException(s"$url does not match $reStr")
    }
  }

  def numberOfViews(views: Int) = {
    val end = {
      val rem100 = views % 100
      if(5 <= rem100 && rem100 <= 20) "ов"
      else {
          val rem10 = views % 10
          if(rem10 == 1) ""
          else if(2 <= rem10 && rem10 <= 4) "а"
          else "ов"
        }
    }
    "$views просмотр$end"
  }
Работа с регулярными выражениями в Scala

Работа с регулярными выражениями в Scala
  if(args.size < 3) {
    println("Usage: mostviewed <domain> <date> <number>")
    sys.exit(1)
Работа с регулярными выражениями в Scala
  }

  val Array(domain, date, numberStr, _*) = args
  val number = numberStr.toInt

  println("<ul>")
  for((url, views) <- getStat(domain, date)
                        .filter(_._1 != s"http://${domain}/")
                        .take(number)) {
    println(s"""<li><a href="${url}">${getTitle(url)}</a>, """ +
            s"${numberOfViews(views)} в месяц</li>")
  }
  println("</ul>")
}

Давайте попробуем в ней разобраться:

  def buildStatUrl(domain: String, date: String) = {
    s"http://www.liveinternet.ru/stat/${domain}/" +
     s"pages.html?date=${date}&period=month&total=yes&per_page=100"
  }

  def fetchStatUrl(domain: String, date: String) = {
    val url = buildStatUrl(domain, date)
    Source.fromURL(url).mkString
  }

Здесь для нас нет ничего нового. Интерполяция строк и объект-одиночка Source нам уже знакомы.

Работа с регулярными выражениями в Scala
  def getStat(domain: String, date: String) = {
    val data = fetchStatUrl(domain, date)
    val re = """(?s)for="id_\d+"><a href="([^"]+)"[^>]*>.*?<td>([\d,]+)</td>""".r
    for (re(url, count) <- re findAllMatchIn data)
      yield (url, count.split("").filter(_ != ",").mkString.toInt)
  }

Строчки в Scala имеют способ .r, который компилирует строчку в постоянное выражение (экземпляр класса Regex). Синтаксис постоянных выражений в Scala таковой же, как и в других языках. Благодаря тройным кавычкам мы можем писать постоянные выражения, не экранируя оборотные слэши, как это приходится делать в Java.

Класс Regex имеет способы findAllIn, findAllMatchIn, replaceAllIn и другие. Полный список способов и их описание вы отыщите в официальной документации. Синтаксис Scala позволяет использовать способы, как операторы.

Другими словами, выражения re findAllMatchIn data и 1 + 2 эквивалентны re.findAllMatchIn(data) и (1).+(2) соответственно. Исключение из этого правила составляют способы, имена которых завершаются двоеточием. Эти способы правоассоциативны.

Другими слоавами, 1 :: Nil эквивалентно Nil.::(1). Если вы недоумеваете, почему так, непременно помедитируйте над этим вопросом, это занимательно.

Оператор for в Scala имеет двойное предназначение. Он предназначен как для объявления циклов, так и для подмены генераторов списков. К примеру, так объявляются обыкновенные for-циклы:

scala> for(x <- Один to 3) println(x)
1
2
3

scala> for(x <- Четыре until Один by -1) println(x)
4
3
2

А так в Scala смотрятся генераторы списков:

scala> for (x <- Один to 3) yield x
res0: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 3)

scala> for     y = x * x
yield (x, z)
res1: scala.collection.immutable.IndexedSeq[(Int, Int)] = Vector((1,7), (2,4), (3,6))

Беря во внимание вышеупомянутое, вы должны с легкостью прочесть код:

Работа с регулярными выражениями в Scala
    for (re(url, count) <- re findAllMatchIn data)
      yield (url, count.split("").filter(_ != ",").mkString.toInt)

В строке data находятся все совпадения с постоянным выражением re. Постоянные выражения в Scala также являются и экстракторами, по этому мы просто и непосредственно помещаем в переменные url и count совпадения, надлежащие первой и 2-ой паре скобочек в re соответственно.

Потом для каждого отысканного совпадения мы генерируем пару (кортеж из 2-ух частей), содержащую url, также count, из которого отфильтровываются запятые, после этого count преобразуется в число:

scala> val count = "1,234"
count: java.lang.String = 1,234

scala> count.split("").filter(_ != ",").mkString.toInt
res2: Int = Одна тыща двести 30 четыре

Кортежи в Scala похожи на кортежи в Haskell. Они неизменяемы, и, в отличии от списков, могут содержать элементы разных типов:

scala> val t = (123, "aaa")
t: (Int, java.lang.String) = (123,aaa)

scala> t._1
res27: Int = 123

scala> t._2
res28: java.lang.String = aaa

scala> t._1 = 456
<console>:8: error: reassignment to val
       t._1 = Четыреста 50 6

Для доступа к N-му элементу кортежа употребляется способ _N. Нумерация частей начинается с единицы.

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

Дополнение: Строим диаграммы при помощи Scala Chart

Похожие статьи

  • Памятка по постоянным выражениям

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

    1. ВведениеПара слов д...

  • Работа с Excel-файлами в Scala

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

  • Шпаргалка по работе с DBIxClass

    Отлично обмысленный ORM может значительно упростить жизнь программеру. Но если это так, то откуда берутся клики, что «ORM — это антипаттерн»? Думается, дело в том, что не все ORM идиентично неплохи...

  • Моя 1-ая программа8230 в сей раз на Scala

    Перепробовав странноватые и никому на практике ненадобные Haskell, OCaml, Perl 6, Common Lisp, Vala, Go, также малость D, что, вобщем, не было отражено в этом блоге, я решил направить свое внимание на...

  • Работа с XML в Haskell и фреймворке Yesod

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

Теги:
Рейтинг: +5 Голосов: 241 1481 просмотр
Комментарии (0)

Нет комментариев. Ваш будет первым!

Найти на сайте: параметры поиска

Windows 7

Среда Windows 7 на первых порах кажется весьма непривычной для многих.

Windows 8

Если резюмировать все выступления Microsoft на конференции Build 2013.

Windows XP

Если Windows не может корректно завершить работу, в большинстве случаев это

Windows Vista

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