Browsed by
Месяц: Ноябрь 2016

Эффект вычисления строки в JavaScript

Эффект вычисления строки в JavaScript

Я как-то писал о jQuery плагине, который создает запоминающиеся пароли.  Основа его привлекательности кроется в интересном эффекте: новый пароль как бы вычисляется на глазах у зрителя.

 Strong-password jQuery plugin

Сегодня расскажу, как сделать такой эффект.

Создадим функцию generate, которая будет создавать наш эффект вычисления строки:


    var generate = function(str, result, from, delay) {
    //   Здесь будет наш код
   }

Наша функция принимает четыре пораметра, первые два — обязательные.

str — это та строка, которая будет показана в конце нашей анимации (результат вычислений)

result — это html элемент,  для удобства уже «завернутый» в jQuery объект.

from —  это строка, состоящая из символов, участвующих в нашей анимации. Если не определена, то  'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz !'

delay —  это задержка между изменениями. Если не определена, то 100.

Наша функция не будет возвращать никаких значений, а только вносить изменения в текст на экране.

Прежде всего, нам нужна служебная функция, которая «перемешивает» элементы массива:


    var shuffle = function(a) {
        var i, j, x;
        j = void 0;
        x = void 0;
        i = void 0;
        i = a.length;
        while (i) {
          j = Math.floor(Math.random() * i);
          x = a[i - 1];
          a[i - 1] = a[j];
          a[j] = x;
          i -= 1;
        }
      };

Далее нам понадобятся две функции для рекурсии.


    var outerSort = function() {
        var letter, letters, span;
        if (strArray.length === 0) {
          return;
        }
        span = $('').appendTo(result);
        letter = strArray.shift();
        letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz !'.split('');
        if(from) letters = from.split('');
        shuffle(letters);
        innerSort(letters, letter, span);
        outerSort();
      };



      var innerSort = function(letters, letter, span) {
        var x;
        if (!letters || letters.length === 0) {
          return;
        }
        x = letters.shift();
        span.html(x);
        setTimeout((function() {
          if (x === letter) {
            span.addClass('sp-generated');
          } else {
            innerSort(letters, letter, span);
          }
        }), delay);
      };

Функция outerSort использует внешнюю по отношению к ней переменную strArray в которую попадает строка str, но уже в виде массива символов:


    strArray = str.split('');
    outerSort();

Функция забирает первый элемент из массива (при помощи shift), создает для него span=$('<span>').appendTo(result), делает из from массив и перемешивает его с помощью нашей функции shuffle. Затем она передает все это в функцию innerSort и вызывается снова.

Функция innerSort перебирает массив символов с заданным интервалом delay до тех пор, пока не будет совпадения.

Теперь нам осталось вызвать нашу функцию:


    var thanks = $('#thanks');
    thanks.click(function(){
      $(this).empty();
      generate('Thank you a lot!', $(this));
    });

    var pi = $('#pi');
    pi.click(function(){
      $(this).empty();
      generate('3.1415926535', $(this), '0123456789.', 100);
    });

Это работает вот так:

Данный пример можно посмотреть на Codepen

 

Полный код:



    var generate = function(str, result, from, delay) {
      if(!delay) delay = 50;
          var strArray;

          var shuffle = function(a) {
            var i, j, x;
            j = void 0;
            x = void 0;
            i = void 0;
            i = a.length;
            while (i) {
              j = Math.floor(Math.random() * i);
              x = a[i - 1];
              a[i - 1] = a[j];
              a[j] = x;
              i -= 1;
            }
          };


          var outerSort = function() {
            var letter, letters, span;
            if (strArray.length === 0) {
              return;
            }
            span = $('').appendTo(result);
            letter = strArray.shift();
            letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz !'.split('');
            if(from) letters = from.split('');
            shuffle(letters);
            innerSort(letters, letter, span);
            outerSort();
          };
          var innerSort = function(letters, letter, span) {
            var x;
            if (!letters || letters.length === 0) {
              return;
            }
            x = letters.shift();
            span.html(x);
            setTimeout((function() {
              if (x === letter) {
                span.addClass('sp-generated');
              } else {
                innerSort(letters, letter, span);
              }
            }), delay);
          };

          strArray = str.split('');
          outerSort();
        };

    var thanks = $('#thanks');
    thanks.click(function(){
      $(this).empty();
      generate('Thank you a lot!', $(this));
    });

    var pi = $('#pi');
    pi.click(function(){
      $(this).empty();
      generate('3.1415926535', $(this), '0123456789.', 100);
    });

 

Learn You Scala for Great Good! Часть 3

Learn You Scala for Great Good! Часть 3

Сегодня я продолжу разговор, начатый здесь (пример про Гарды и калькулятор лишнего веса), и продолженный здесь (вычисление максимального числа из списка) и здесь (пример про максимум для произвольного списка). Все это касалось изучения функционального программирования по книге Learn You a Haskell for Great Good!

Функция replicate

Следующая функция из главы, посвященной рекурсии, называется replicate. Она получает в качестве первого аргумента число повторений, а в качестве второго — элемент, который нужно повторить нужное число раз. И возвращает список, состоящий из повтореного нужное число раз элемента.

Итак, Haskell:


    replicate :: (Num i, Ord i) => i -> a -> [a]  
    replicate n x  
        | n <= 0    = []  
        | otherwise = x:replicate (n-1) x

Как и в предыдущем примере, мы видим typeclass Ord, которого нет в Scala (но не в сторонних библиотеках).

Сперва напишем пример для целых чисел:


    def replicate(i:Int, x: Int):List[Int] = {
      i match {
        case n if n <= 0 => Nil
        case n => x::replicate(n-1, x)
      }
    }
    println(replicate(7, 9))

Но мы можем легко привести эту функцию к общему виду, заменив Int на Any:


    def replicate(i:, x: Any):List[Any] = {
      i match {
        case n if n <= 0 => Nil
        case n => x::replicate(n-1, x)
      }
    }

    println(replicate(12, 'a'))

Хотя правильнее будет записать это так:


    def replicate[A](i:, x: A):List[A] = {
      i match {
        case n if n <= 0 => Nil
        case n => x::replicate(n-1, x)
      }
    }

    println(replicate(12, 'a'))

Теперь функция будет работать с определенным типом, а не с Any.

 Функция take

Эта функция принимает число n и список l и возвращает другой список, содержащий первые n элементов списка  l.
Haskell:


    take :: (Num i, Ord i) => i -> [a] -> [a]  
    take n _  
        | n <= 0   = []  
    take _ []     = []  
    take n (x:xs) = x : take (n-1) xs  

В Scala это выглядит так:


    def take[A](n:Int, l:List[A]): List[A] = {
      (n, l) match {
        case (n, _) if n <= 0 => Nil
        case (_, Nil) => Nil
        case (n, x::xs) => x::take(n-1, xs)
      }
    }

    println(take(3, List(5, 34, 99, 8, 567, 222)))
    println(take(5, List("5", "34", "99", "8", "567", "222")))

Используется сравнение с образом (pattern matching):

  • Если n равно или меньше нуля, мы получаем пустой лист.
  • Если второй аргумент — пустой лист, то возвращается пустой лист.
  • Если же второй аргумент список x::xs, то функция вызывается снова x::take(n-1, xs)

 

Функция reverse

Данная функция просто «переворачивает» список:

Haskell:


    reverse :: [a] -> [a]  
    reverse [] = []  
    reverse (x:xs) = reverse xs ++ [x]

Scala:


    def reverse[A](l: List[A]):List[A] = {
      l match {
        case Nil => Nil
        case x::xs => (reverse(xs) ::: x::Nil)
      }

    }

    println(reverse(1::2::3::4::4::5::6::7::Nil))

Все очень просто: функция берет первый элемент списка и бросает его в конец, затем вызывается рекурсивно к оставшейся части.

 

Анимированный генератор паролей

Анимированный генератор паролей

Хочу рассказать, как просто добавить ненавязчивый генератор сильных паролей на сайт.
Самый простой способ — это воспользоваться бесплатным jQuery плагином strong-password.js, который легко можно скачать на GitHub.

 Strong-password jQuery plugin

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

Использование.
1. Скачиваем с GitHub.
2. Подключаем необходимые библитеки на страницу:


    <link rel="stylesheet" href="strong-password/css/strong-password.min.css">
    <script type="text/javascript" src="lib/jquery-2.2.2.min.js"></script>
    <script type="text/javascript" src="strong-password/js/strong-password.min.js"></script>

3. Подключаем плагин к полю формы:


    $('input[type=password]').strongPassword();

 

Можно также добавить опции, тогда:


    var options = {
      cssClass: 'sp-black',
      backdrop: false
     // other options...
    }
    $('input[type=password]').strongPassword(options);

Посмотреть все настройки можно здесь.
Также посмотреть можно на codepen.io.

Лирическое отступление

Лирическое отступление

Когда я писал предыдущий пост, я не стал создавать функцию maximum в общем виде, а рассмотрел лишь ее частный случай для List[Int].

Впоследствии я подумал: а почему бы собственно и нет? Тем более что, покопавшись на StackOverflow, я нашел сразу два способа, как это сделать.

В чем проблема

Проблема в том, что мы не можем просто написать общую (generic) функцию:


    def maximum[A](l:List[A]):A = {
      l match { case Nil => throw new Exception("Nil")
        case x::Nil => x
        case x::xs if x > maximum(xs) => x
        case x::xs => maximum(xs)
      }
    }

Компилятор выдаст ошибку:
error: value > is not a member of type parameter A

Наш тип A не имеет метода > !
Очевидно, что наш общий (generic) тип нужно привести к чему-то, что имеет механизм сравнения.
В скале есть 2 трэйта scala.Ordered и scala.math.Ordering, обладающие необходимыми нам методами.

View Bound

Хотя данный способ считается устаревшим (deprecated), он довольно нагляден и понятен:

    
    def maximum[A <% Ordered[A]](l:List[A]):A = {
      l match { case Nil => throw new Exception("Nil")
        case x::Nil => x
        case x::xs if x > maximum(xs) => x
        case x::xs => maximum(xs)
      }
    }

    println(maximum[Char]('f'::'a'::'h'::Nil))

В нашем примере Char не является Ordered, поэтому происходит неявное (implicit) преобразование. Мы как бы заставляем компилятор принимать Char за Ordered.

Context Bounds

При помощи этого подхода можно получить в Scala что-то похожее на type classes из Haskell.


    def maximum[A](l: List[A])(implicit ord: Ordering[A]): A =
      l match {
        case Nil => throw new Exception("maximum of empty list")
        case x :: Nil => x
        case x :: xs => ord.max(x, maximum(xs))
    }

    println(maximum[Char]('f'::'a'::'h'::Nil))

Наша функция принимает неявный (implicit) параметр ord имеющий тип Ordering[A].

Все рабтает. Но возникает вопрос, откуда функция берет этот параметр?

Дело в том, что объект-компаньон Ordering содержит большой набор преобразований для различных типов.

Learn You Scala for Great Good! Часть 2

Learn You Scala for Great Good! Часть 2

В прошлый раз я начал делиться своим опытом изучения основ функционального программирования по книжке Learn You a Haskell for Great Good! применительно к языку программирования Scala.

Сегодня я разберу пример из главы, посвященной рекурсии.

Нахождение максимального числа в списке

Здесь сразу хочу заметить, что пример на Haskell является более общим и находит наибольшее число от чего угодно, что може быть отсортировано (of things that can be ordered).  Достаточно, чтобы члены списка были экземплярами тайпкласса Ord. В Scala, чтобы в полной мере использовать тайпклассы, нужно обратиться к таким библтотекам, как Scalaz или Cats. Я здесь не буду этого делать.  Мой пример на Scala будет для List[Int]

Итак, Haskell:


    maximum :: (Ord a) => [a] -> a  
    maximum [] = error "maximum of empty list"  
    maximum [x] = x  
    maximum (x:xs)   
        | x > maxTail = x  
        | otherwise = maxTail  
        where maxTail = maximum xs

Применение функции maximum к пустому списку выдаст ошибку.

Применение к списку из одного элемента вернет этот элемент.

В общем случае используется сравнение с образом (head:tail) и строится рекурсия:

Если head (первый элемент списка) больше, чем наибольший элемент из tail (остальной части списка), то приисходит выход из рекурсии. В противном случае функция вызывается снова с параметром tail.

На Scala это выглядит так:


    def maximum(l:List[Int]):Int = {

      l match {
        case Nil => throw new Exception("maximum of empty list")
        case x::Nil => x
        case x::xs if x > maximum(xs) => x
        case x::xs => maximum(xs)
      }
    }

Еще раз повторюсь, что здесь мы сравниваем целые числа.

см. Лирическое отступление

Продолжение следует…

Increment, decrement

Increment, decrement

В Scala нет операторов ++ и --

Пишут, что это связано с тем, что поле val не может менять своего значения (immutable) в отличие от var.

Но можно делать следующее:


var x = 4
x += 1
x -= 2
x *= 2
x /= 3

В Scala +=, -=, *=, /= это методы, а не операторы. Они есть в разных классах, например  Int, Double, Float, String, но в каждом классе они свои.

Следует помнить, что для  val применение этих методов выдаст ошибку.


//Так нельзя:
val y ="Oops"
y += "!"

 

Learn You Scala for Great Good!

Learn You Scala for Great Good!

Я пришел в Scala из Java. Поэтому, когда я решил немного разобраться с такими библиотеками, как Scalaz или Cats, встал вопрос более глубокого изучении принципов функционального программирования. Лучшее, что я нашел, была книга Learn You a Haskell for Great Good!

Да, чтобы разобраться в функциональном программировании для Scala, пришлось познакомиться с совершенно новым для меня языком Haskell.

Чтобы лучше разбираться в написанном, я попутно «переводил» для себя некоторые примеры на Scala (по крайней мере, там где возникали какие-то аналогии).

На случай, если кто-то пойдет по моему пути, я решил поделиться своими результатами.

Guards, guards!

Первый пример из 4 главы посвящен использованию Гардов (сейчас лень искать русский аналог этого термина) в Haskell.


    bmiTell :: (RealFloat a) => a -> a -> String  
    bmiTell weight height  
        | weight / height ^ 2 <= 18.5 = "You're underweight, you emo, you!"  
        | weight / height ^ 2 <= 25.0 = "You're supposedly normal. Pffft, I bet you're ugly!"  
        | weight / height ^ 2 <= 30.0 = "You're fat! Lose some weight, fatty!"  
        | otherwise                 = "You're a whale, congratulations!"  

 

Здесь создается функция, которая вычисляет коэффициент BMI (используется для определения лишнего веса) с использованием гардов (guard). Если ваш BMI меньше 18.5, то вы излишне худы. Если от 18.5 до 25.0, то вес в норме. Если выше, то у вас лишний вес. А если больше 30, то вы — кит (так написано в первоисточнике).

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

Приблизительно вот так это выглядит в scala:



def rate(w:Int, h:Double):String = {
  val ratio = w/pow(h,2)
  ratio match {
    case r if r <= 18.5 => s"Underweight $r"
    case r if r <= 25 => s"Normal $r"
    case r if r <= 30 => s"Overweight! $r"
    case r => s"Whale! $r"

  }
}

println(rate(78, 1.76))

Похоже? По-моему, да. В Scala Гарды начинаются с ключевого слова if. А вместо otherwise используется паттерн без гарда. А так, код выглядит почти идентично.

 

Продолжение следует…