________fear_of_underscores

пятница, 19 октября 2012 г.

Фича F#, о которой я не знал

Иногда бывает нужно воспользоваться в pipe-lineing'e каррированной функцией, но на этом этапе вычислений мы уже работаем с кортежем. Как быть? В F# есть четыре прекрасных оператора для этого. Вот два из них. Два других угадайте сами :)

А вот примерчик:
List.partition (fun x -> x % 2 = 0) [1..10] ||> (@)
//val it : int list = [2; 4; 6; 8; 10; 1; 3; 5; 7; 9]
P.S. Читаю только что купленную Programming F# 3.0 — масса наслаждения и няшности.

вторник, 25 сентября 2012 г.

F#un tee pipe

В Linux есть прекрасная команда tee, которая берёт вывод какой-то команды, записывает его в файл, и в то же время выводит на консоль:

А ещё в Linux shell есть pipe-lining, очень похожий на pipe-lining из F#.

Эти два обстоятельства и одна задачка, возникшая на работе, натолкнули меня на мысль, что в    F# можно добавить аналог tee. Вот, например, такой код:
open System

let rnd = new Random()
[| for _ in 0..100 -> rnd.Next(100) |] .|> "array.txt" |> Array.sum |> printfn "%i"
создаст массив из случайных целых чисел, запишет его в файл "array.txt", посчитает сумму элементов массива и напечатает её на консоль. Осталось только реализовать этот магический оператор (.|>)!

К счастью, в F# подобные задачки решаются тривиально:
open System.IO

let inline ( .|> ) data fileName =
    File.WriteAllLines(fileName, Seq.map (sprintf "%A") data)
    data

//val inline ( .|> ) : 'a -> string -> 'a when 'a :> seq<'b>
Обратите внимание: сия магия работает только с коллекциями (для меня это самый частый use case). На самом деле, можно использовать WriteAllText и sprintf "%A" data соответственно, но тогда слишком длинные коллекции будут обрубаться.

четверг, 13 сентября 2012 г.

Извлечение ключевых слов текста с помощью TF-IDF метрики

Что такое TF-IDF рассказано в этих прекрасных видео с Coursera (Web Intelligence and Big Data):
TF-IDF
TF-IDF Example

Я же покажу реализацию извлечения ключевых слов с помощью этой метрики на F#:
#if INTERACTIVE
#r @"..\packages\HtmlAgilityPack.1.4.6\lib\Net45\HtmlAgilityPack.dll"
#endif

open System
open System.Collections.Generic
open System.Collections.Concurrent
open System.Linq
open System.Text
open System.Text.RegularExpressions
open System.IO
open System.Net
open Microsoft.FSharp.Control.WebExtensions
open HtmlAgilityPack

let makeTFMap (text : string array) =
    let dict = new Dictionary<_, _>()
    for word in text do
        if dict.ContainsKey word then
            dict.[word] <- dict.[word] + 1
        else
            dict.Add(word, 1)
    dict

let IDF (uniqueTokens : string array) =
    let logWebSizeEstimate = Math.Log(5e10, 2.)
    let dict = new ConcurrentDictionary<_, _>()
    seq {
        for word in uniqueTokens ->
            async {
                let req = WebRequest.Create(@"http://www.google.com/search?q=" + word) :?> HttpWebRequest
                req.Timeout <- 5000
                req.UserAgent <-
                    "Mozilla/4.1 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322)"
                let! resp = req.AsyncGetResponse()
                use stream = (resp :?> HttpWebResponse).GetResponseStream()
                use reader = new StreamReader(stream)
                let! html = reader.AsyncReadToEnd()
                let doc = new HtmlDocument()
                doc.LoadHtml html
                let stats = doc.DocumentNode.SelectSingleNode(@"//div[@id = 'resultStats']").InnerText
                let numStr = Regex.Match(stats, @"[0-9,]+").Value.Replace(",", "")
                let idf = logWebSizeEstimate - Math.Log(float numStr, 2.)
                dict.AddOrUpdate(word, idf, (fun _ _ -> 0.)) |> ignore
            }
    }
    |> Async.Parallel
    |> Async.RunSynchronously |> ignore
    dict

let TF_IDF (tf : Dictionary<_, _>) (idf : ConcurrentDictionary<_, _>) =
    let uniqueTokens = tf.Keys.ToArray()
    dict [ for word in uniqueTokens ->  word, float tf.[word] * idf.[word] ]

let analyze (text : string) =
    let tokens = text.Split([|' '; '\n'; '\t'; '.'|], StringSplitOptions.RemoveEmptyEntries)
    let tf = makeTFMap tokens
    TF_IDF tf (IDF <| tf.Keys.ToArray())

let printTop text num =
    let results = analyze text
    let hot10 =
        Array.sortBy (fun (kvp : KeyValuePair<_, _>) -> kvp.Value) (results.ToArray())
        |> Array.rev |> Seq.take num
    for kvp in hot10 do
        printfn "%s has TF-IDF of %f" kvp.Key kvp.Value
Пример использования:
let article = 
    "Акции протеста против антиисламского фильма распространяются по всему миру: демонстрации прошли в Ливии,
     Йемене, Египте, Ираке, Тунисе и других странах. Власти Афганистана и Пакистана закрыли доступ к YouTube,
     дабы попытаться не допустить просмотра скандального фильма. Участники акций протеста считают, что
     размещенный в интернете неким 52-летним американцем из Калифорнии любительский фильм, оскорбляет пророка
     Мухаммеда. При штурме американского посольства в Ливии во время акции протеста 
     во вторник погиб американский посол."
printTop article 10
(Текст взят из этой статьи BBC)
Результат:
протеста has TF-IDF of 30.083060
антиисламского has TF-IDF of 18.183107
штурме has TF-IDF of 15.472137
американцем has TF-IDF of 14.996109
Йемене, has TF-IDF of 14.777763
оскорбляет has TF-IDF of 14.299300
скандального has TF-IDF of 14.253497
Тунисе has TF-IDF of 14.058740
размещенный has TF-IDF of 13.757642
неким has TF-IDF of 13.116506
Real: 00:00:09.695, CPU: 00:00:01.248, GC gen0: 41, gen1: 16, gen2: 1

среда, 12 сентября 2012 г.

F#un code generation

На работе (Erlang/C#-разработка) подозрительно часто приходится писать небольшие скрипты для кодогенерации. Пишу я их, конечно, на F# — и он успел зарекомендовать себя как отличный инструмент для этого (кто бы сомневался!). Сейчас же я немного покодогенерировал не на работе, а делая новую версию моего шаблона проекта DataAnalysisApp.

Задача: сделать кодогенерацию директив компилятора F# для подключения .dll-библиотек из пакетов NuGet'a.

Эти библиотеки расположены в подпапках \lib директории с пакетами, а иногда — в подпапках этих подпапок с названиями вида \Net45 или \40.

Решение:
let printInterpreterRDirectives dir =
    let formDirective path =
        sprintf """#r @"..%s" """ <| Regex.Match(path, @"(?i:\\packages\\.*\\lib\\.*)").Value
    
    Directory.EnumerateDirectories dir
    |> Seq.map (Directory.EnumerateDirectories >> Seq.find (fun y -> Regex.IsMatch(y, @"(?i:\\lib$)")))
    |> Seq.collect (fun dir -> 
                        seq {
                            for subdir in Directory.EnumerateDirectories dir do
                                if Regex.IsMatch(subdir, @"(?i:\\(net)?4(0|5)$)") then 
                                    yield subdir
                            yield dir
                        })
    |> Seq.collect (Directory.EnumerateFiles >> Seq.filter (fun file -> FileInfo(file).Extension = ".dll"))
    |> Seq.map formDirective
    |> Seq.iter (printfn "%s")

printInterpreterRDirectives @"C:\...\Visual Studio 2012\Projects\Data Analysis App 2012\packages"
Получается портянка из вот такого:

#r @"..\packages\FSPowerPack.Community.2.1.3.1\Lib\Net40\FSharp.PowerPack.Parallel.Seq.dll"
#r @"..\packages\HtmlAgilityPack.1.4.6\lib\Net45\HtmlAgilityPack.dll"
#r @"..\packages\MathNet.Numerics.2.2.1\lib\Net40\MathNet.Numerics.dll"
...


Довольно красивое сочетание pipe-lining'a, композиции и вычислительного выражения. Ну и регэкспы, конечно!

Легко заметить, что в решении используется одна из новых возможностей F#: triple-quoted strings. Кстати, скоро будет пост с анализом этих самых новых возможностей))
Ну и да — на самом деле можно сделать проще, воспользовавшись методом EnumerateDirectories с другой арностью:
let printInterpreterRDirectives' dir =
    let formDirective path =
        sprintf """#r @"..%s" """ <| Regex.Match(path, @"(?i:\\packages\\.*\\lib\\.*)").Value
    let enumSubdirs pattern = Directory.EnumerateDirectories(dir, pattern, SearchOption.AllDirectories)
    
    List.fold (fun acc pattern -> Seq.append acc <| enumSubdirs pattern) Seq.empty
              ["lib"; "net40"; "net45"; "40"; "45"]
    |> Seq.collect (Directory.EnumerateFiles >> Seq.filter (fun file -> FileInfo(file).Extension = ".dll"))
    |> Seq.map formDirective
    |> Seq.iter (printfn "%s")

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

пятница, 7 сентября 2012 г.

Простая задачка

Given that Pi can be estimated using the function 4 * (1 – 1/3 + 1/5 – 1/7 + …) with more terms giving greater accuracy, write a function that calculates Pi to an accuracy of 5 decimal places.
Моё решение
let pi precision =
    let rec helper pre curr digit sign =
        if Math.Abs(pre * 4. - curr * 4.) < precision then
            4. * curr
        else
            helper curr ((if sign then (+) else (-)) curr 
                   (1. / (digit + 2.))) (digit + 2.) (not sign)
    helper Double.PositiveInfinity 1. 1. false
pi 0.00001 |> printfn "Estimated Pi: %f"
Сможете решить короче? ;)

среда, 5 сентября 2012 г.

F#un for Kids

Функция — это штука, которой мы даём что-то, она что-то с этим делает, и что-то отдаёт нам обратно.

Тип функции — это описание того, что ей нужно дать и что она вернёт.

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

среда, 25 июля 2012 г.

Фишки F#

Params в F# (функции с переменным числом параметров)

Работая в F#, я ни разу не сталкивался с необходимостью использовать нечто в этом роде, но тем не менее оно есть. У F# нет ключевого слова для создания таких функций, но мы можем использовать для этого атрибуты:

//using params analogue in F#
type SomeBoilerplateType =
    static member Add ([<ParamArray>] x) = x |> (Array.map float) |> Array.sum

SomeBoilerplateType.Add(1, 2)
SomeBoilerplateType.Add(1,2,3,4,5)
SomeBoilerplateType.Add([|for i in 0..10 -> i|])
Как вы думаете, какого типа будет такой метод? :)
Конечно, int array -> int.
Печально, что использовать это можно только в методах классов (как и, например, ad hoc-полиморфизм (секция "Overloaded Methods"), именованные и опциональные аргументы (перейдя по последней ссылке, промотайте вниз :) )).
Кстати, есть ещё кое-какие тонкости.

Индексаторы

Индексаторы являются "официальной" фишкой F#, только называются они не Indexers, как в C#, а Indexed Properties. На MSDN написано про них практически всё, кроме одной детали: вместо того, чтобы именовать свойство, используемое для индексирования, как Item, можно дать ему любое другое имя, но при этом также задать классу атрибут [<DefaultMember("name")>], где Name — имя свойства.
Пример с Item:
//indexers for F#
type Slowpoke() = 
    let hidden = [|for i in 0..10 -> i|]
    let mutable current = 0
    member this.Item 
        with get(_) = 
            let res = hidden.[current]
            current <- (current + 1) % hidden.Length
            System.Threading.Thread.Sleep(1000)
            res

let slowpoke = new Slowpoke()
slowpoke.[666]
slowpoke.[666]
Пример с атрибутом (заодно — другой вариант синтаксиса для свойства без сеттера):
open System.Reflection
[<DefaultMember("AnotherName")>]
type Slowpoke'() = 
    let hidden = [|for i in 0..10 -> i|]
    let mutable current = 0    
    member this.AnotherName(_) = 
        let res = hidden.[current]
        current <- (current + 1) % hidden.Length
        System.Threading.Thread.Sleep(1000)
        res

let slowpoke' = new Slowpoke'()
slowpoke'.[156]
Кстати, на самом деле, для примера с Item компилятор просто сам добавляет этот атрибут:

typeof<Slowpoke>.GetCustomAttributes(false)
> [|System.Reflection.DefaultMemberAttribute
      {MemberName = "Item";
       TypeId = System.Reflection.DefaultMemberAttribute;}; ... |]

Функции с аргументами-типами (Type Functions)

Кстати, о typeof. Это функция. Не не простая, а так называемая Type Function — функция, которая принимает в качестве своих аргументов типы, а не какие-то значения. Такие функции можно объявлять только в модулях, в классах же, типах и выражениях их объявлять нельзя.
Например, мы можем объявить вот такую функцию:
let set = new System.Collections.Generic.HashSet()
let hasBeenAlreadyCalledWithThisType<'T> = not <| set.Add(typeof<'T>.Name)

hasBeenAlreadyCalledWithThisType<int>   //false
hasBeenAlreadyCalledWithThisType<float> //false
hasBeenAlreadyCalledWithThisType<int>   //true
Эта функция будет вести список всех типов, с которыми она была вызвана и отвечать true/false в зависимости от того, "знаком" ли ей уже текущий тип.
Подробнее о type functions вы можете почитать в спецификации F#.

[<AutoOpen>]

Я просто процитирую спецификацию:
When applied to an assembly and given a string  argument, causes the namespace or module to be opened automatically when the assembly is referenced.  When applied to a module without a string argument, causes the module to be opened automatically when the enclosing namespace or module is opened. 

Компилятор, голос!

Можно заставить компилятор выдавать заданные нами сообщения с помощью атрибута
[<CompilerMessage("SomeUserSpecifiedMessage", MessageNumber)>].


P.S.: Были кое-какие проблемы с отображением кода F#, я решил их. При использовании F# brush для SyntaxHighlighter'а нужно закомментить строки про symbolic-класс, иначе будут глюки.

среда, 20 июня 2012 г.

MS Visual Studio Template for Data Analysis

Уже довольно давно я пользуюсь няшным шаблоном для VS, который помогает в пару кликов мышки создать проект F#, в котором легко можно делать матричные операции с помощью Math.Net Numerics, использовать HtmlAgilityPack и рисовать графики с помощью FSharpChart:


Я сделал этот шаблон, когда понял, что если ещё один раз буду вручную добавлять все нужные библиотеки — повешусь. Тем не менее, сам процесс создания шаблона не обошёлся без пары gotchas, так что я решил записать его; сейчас же перепишу его сюда и приложу сам шаблон, на благо общественности.

В шаблоне используются библиотеки, добытые с помощью NuGet'a и скрипт FSharpChart. Все права на всё принадлежат тем, кому они принадлежат ^_^.

АлгоритмЪ:
  1. Создать обычный проект F#, включить в него все нужные библиотеки и файлы, загрузить все нужные NuGet-пакеты, написать весь boilerplate код. В моём случае это выглядело так:
    #if INTERACTIVE 
    //Math.Net Numerics
    #r @"..\packages\MathNet.Numerics.2.1.2\lib\Net40\MathNet.Numerics.dll"
    #r @"..\packages\MathNet.Numerics.FSharp.2.1.2\lib\Net40\MathNet.Numerics.FSharp.dll"
    //PowerPack
    #r @"..\packages\FSPowerPack.Community.2.1.1.1\Lib\Net40\FSharp.PowerPack.dll"
    #r @"..\packages\FSPowerPack.Community.2.1.1.1\Lib\Net40\FSharp.PowerPack.Linq.dll"
    #r @"..\packages\FSPowerPack.Community.2.1.1.1\Lib\Net40\FSharp.PowerPack.Metadata.dll"
    #r @"..\packages\FSPowerPack.Community.2.1.1.1\Lib\Net40\FSharp.PowerPack.Parallel.Seq.dll"
    //FSharpChart
    #r @"..\packages\MSDN.FSharpChart.dll.0.60\lib\MSDN.FSharpChart.dll"
    #load "FSharpChart.fsx"
    //HtmlAgility
    #r @"..\packages\HtmlAgilityPack.1.4.3\lib\HtmlAgilityPack.dll"
    #r @"System.Xml"
    #r @"System.Xml.Linq"
    #endif
    
    open System
    open System.Collections.Generic
    open System.Linq
    open System.Text
    open System.IO
    open System.Drawing
    open System.Windows.Forms
    open System.Windows.Forms.DataVisualization.Charting
    open System.Xml
    open System.Xml.Linq
    
    open MathNet.Numerics
    open MathNet.Numerics.FSharp
    open MathNet.Numerics.LinearAlgebra.Double
    open MathNet.Numerics.LinearAlgebra.IO
    open MathNet.Numerics.Distributions
    open MSDN.FSharp.Charting  //FSharpChart
    open HtmlAgilityPack       //HtmlAgility
    open Microsoft.FSharp.Math //PowerPack
    
  2. File > Export Template...
  3. Получится .zip файл, в корневую папку которого надо вставить все .nupkg файлы:
  4. В файле MyTemplate.vstemplate надо поменять метаданные в разделе TemplateData :
    <TemplateData>
        <Name>Data Analysis App Extended</Name>
        <Description>Data Analysis Application with installed Math.Net Numerics,
             FSharpChart, HtmlAgility and F# PowerPack libraries.</Description>
        <ProjectType>FSharp</ProjectType>
        ...
    </TemplateData>
    
  5. В разделе WizardData нужно добавить информацию о всех используемых NuGet-пакетах:
    <WizardData>
        <packages repository="template">
          <package id="zlib.net" version="1.0.3.0" />
          <package id="MathNet.Numerics" version="2.1.2" />
          <package id="MathNet.Numerics.FSharp" version="2.1.2" />
          <package id="MSDN.FSharpChart.dll" version="0.60" />
          <package id="HtmlAgilityPack" version="1.4.3" />
          <package id="FSPowerPack.Community" version="2.1.1.1" />
        </packages>
      </WizardData>
    
  6. Наконец, в самом файле проекта .fsproj, нужно убедиться, что используются относительные пути для NuGet-пакетов:
    <PropertyGroup>
        ...
        <ReferencePath>..\packages\</ReferencePath>
    </PropertyGroup>
    ...
    <ItemGroup>
        <Compile Include="Program.fs" />
        <None Include="packages.config" />
        <None Include="FSharpChart.fsx" />
    </ItemGroup>
    <ItemGroup>
        <Reference Include="FSharp.PowerPack">
          <HintPath>..\packages\FSPowerPack.Community.2.1.1.1\Lib\Net40\FSharp.PowerPack.dll</HintPath>
          <Private>True</Private>
        </Reference>
        <Reference Include="FSharp.PowerPack.Linq">
          <HintPath>..\packages\FSPowerPack.Community.2.1.1.1\Lib\Net40\FSharp.PowerPack.Linq.dll</HintPath>
          <Private>True</Private>
        </Reference>
        <Reference Include="FSharp.PowerPack.Metadata">
          <HintPath>..\packages\FSPowerPack.Community.2.1.1.1\Lib\Net40\FSharp.PowerPack.Metadata.dll</HintPath>
          <Private>True</Private>
        </Reference>
        <Reference Include="FSharp.PowerPack.Parallel.Seq">
          <HintPath>..\packages\FSPowerPack.Community.2.1.1.1\Lib\Net40\FSharp.PowerPack.Parallel.Seq.dll</HintPath>
          <Private>True</Private>
        </Reference>
        <Reference Include="HtmlAgilityPack">
          <HintPath>..\packages\HtmlAgilityPack.1.4.3\lib\HtmlAgilityPack.dll</HintPath>
          <Private>True</Private>
        </Reference>
        <Reference Include="mscorlib" />
        <Reference Include="FSharp.Core" />
        <Reference Include="System" />
        ...
        <Reference Include="System.Xml" />
        <Reference Include="System.Xml.Linq" />
        <Reference Include="MathNet.Numerics">
          <HintPath>..\packages\MathNet.Numerics.2.1.2\lib\Net40\MathNet.Numerics.dll</HintPath>
          <Private>True</Private>
        </Reference>
        <Reference Include="MathNet.Numerics.FSharp">
          <HintPath>..\packages\MathNet.Numerics.FSharp.2.1.2\lib\Net40\MathNet.Numerics.FSharp.dll</HintPath>
          <Private>True</Private>
        </Reference>
        <Reference Include="MSDN.FSharpChart">
          <HintPath>..\packages\MSDN.FSharpChart.dll.0.60\lib\MSDN.FSharpChart.dll</HintPath>
          <Private>True</Private>
        </Reference>
        <Reference Include="zlib.net">
          <HintPath>..\packages\zlib.net.1.0.3.0\lib\zlib.net.dll</HintPath>
          <Private>True</Private>
        </Reference>
    </ItemGroup>
    
    Значение тега <Private>true — значит, что все .dll будут скопированы в папку создаваемого с помощью шаблона проекта.
  7. Чтобы добавить исправленные файлы обратно в .zip-архив, нужно использовать Windows Explorer — просто выделите и перетащите их на .zip файл. Я понятия не имею, почему не работает добавление с помощью WinRar'а, но я смог добиться успеха в сём предприятии только так.
  8. Осталось только перетащить исправленный .zip архив с шаблоном в папку C:\Users\...\Documents\Visual Studio 2010\Templates\ProjectTemplates, и новый шаблон появится в диалоге создания нового проекта.
Ну и, как и было обещано, сам шаблон (или на github'e).
После создания проекта запустите компиляцию, чтобы VS нашла все библиотеки. Alt+Enter поможет вам проверить работу шаблона:
FSharpChart.Point
    [|
        let gaussian = new Normal()        
        for _ in 1..100 -> gaussian.Sample(), gaussian.Sample() |]

воскресенье, 10 июня 2012 г.

Формулы, подсветка синтаксиса и все-все-все

Так как планирую писать здесь не только о музыке, но и о технике, жизненно необходима поддержка TeX-а и подсветки синтаксиса для языков, которыми пользуюсь или планирую пользоваться: F#, C#, Erlang, Matlab/Octave и Python.

Встроил MathJax:
\[P(E) = {n \choose k} p^k (1-p)^{n-k}\tag{Cthulhu fhtagn!}\label{ref1}\]
Вот таким скриптом:
<script type="text/x-mathjax-config">
MathJax.Hub.Config({
  TeX: { equationNumbers: { autoNumber: "all" } }
});
</script>
<script src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML" type="text/javascript">
</script>

В комментах тоже, конечно, работает, надо просто писать LaTeX-выражения вот так:
\[ ... \] для отдельных формул
\( ... \) для инлайна
И номера с ссылочками: \ref{ref1}, тоже. С разными — \eqref{ref1}.
А чтобы этот скрипт был вам нормально виден, надо было ещё вот это вставить:
<link href="http://alexgorbatchev.com/pub/sh/current/styles/shCore.css" rel="stylesheet" type="text/css"></link>
<!--SYNTAX HIGHLIGHTER BEGINS-->
<link href='http://alexgorbatchev.com/pub/sh/current/styles/shCore.css' rel='stylesheet' type='text/css'/>
<link href='http://alexgorbatchev.com/pub/sh/current/styles/shThemeFadeToGrey.css' rel='stylesheet' type='text/css'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shCore.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushCpp.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushCSharp.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushCss.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushJava.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushJScript.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushPython.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushRuby.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushSql.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushXml.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushPerl.js' type='text/javascript'/>
<script src='http://sites.google.com/site/lakretsh/home/brushErlang.js' type='text/javascript'/>
<script src='http://sites.google.com/site/lakretsh/home/brushFSharp.js' type='text/javascript'/>
<script language='javascript'>
SyntaxHighlighter.config.bloggerMode = true;
SyntaxHighlighter.config.clipboardSwf = &#39;http://alexgorbatchev.com/pub/sh/current/scripts/clipboard.swf&#39;;
SyntaxHighlighter.all();
</script>
<style type="text/css">
.syntaxhighlighter { overflow-y: hidden !important; }
</style>
<!--SYNTAX HIGHLIGHTER ENDS-->

Грязный трюк с brush для F# и Erlang я подсмотрел на сайте другого F#-разработчика xD

Кстати, Erlang:
%% qsort:qsort(List)
%% Sort a list of items
-module(qsort).     % This is the file 'qsort.erl'
-export([qsort/1]). % A function 'qsort' with 1 parameter is exported (no type, no name)
 
qsort([]) -> []; % If the list [] is empty, return an empty list (nothing to sort)
qsort([Pivot|Rest]) ->
    % Compose recursively a list with 'Front' for all elements that should be before 'Pivot'
    % then 'Pivot' then 'Back' for all elements that should be after 'Pivot'
    qsort([Front || Front <- Rest, Front < Pivot])
    ++ [Pivot] ++
    qsort([Back || Back <- Rest, Back >= Pivot]).

F#:
let asynctask = async
{
    let req = WebRequest.Create(url)
    let! response = req.GetResponseAsync()
    use stream = response.GetResponseStream()
    use streamreader = new System.IO.StreamReader(stream)
    return streamreader.ReadToEnd()
}

Потом надо будет добавить ещё Matlab, но пока лень и наплевать)

Печалька заключается только в том, что приходится редактировать код в HTML-редакторе, когда пишу посты с кодом — всякое такое:
<pre class="brush:html">
</pre>

<pre class="brush:f#">
</pre>

<pre class="brush:erl">
</pre>
добавлять на другой вкладке + для публикации html надо использовать вот такие штуки. Ну да ничего.

P.S. Для готовящихся постов по PGM я также добавил экспандеры.
Код экспандера в экспандере! :)
JavaScript:
<script src='https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js'/>
<script type='text/javascript'>
$(document).ready(function()
{
  $(&quot;.exp-content&quot;).hide();
  $(&quot;.exp-head&quot;).click(function()
  {
    $(this).next(&quot;.exp-content&quot;).slideToggle(200);
  });
});
</script>
CSS:
.exp-head {
padding: 1px 15px;
cursor: pointer;
margin: 5px 0px;
font-weight: bold;
}

.exp-content {
padding: 2px 15px;
background-color: whiteSmoke;
}

.exp {
margin: 0px 30px;
background-color: whiteSmoke;
color: black;
}
Использование:
<div class="exp">
<div class="exp-head">Заголовок</div>
<div class="exp-content">Текст</div>
</div>

Металл и вода I

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

В саунддизайне есть ровно одна вещь, которая меня заводит, то самое, что заставило меня потратить около трёх лет на чтение различной литературы по теме, просмотр видеоуроков и бесчисленные ночные часы, собственно, саунддизайна — это модуляция. Я обожаю огибающие. Я болен LFO. Я схожу с ума от слов "keyscaling", "velocity", "cc" и "MIDI-mapping". И из этой моей страсти (ну, не только из неё, но всё же) непосредственно следует моя любовь к двум типам звуков, доминирующим в моём творчестве и доставляющим мне огромное удовольствие самим процессом своего создания: я люблю металлические лязганья, удары, "струны" (раз) и я люблю пэды (два).

Какая же связь между модуляцией и этими типами звуков? Let's take one at a time.

Металл

Металлические звуки в большинстве своём для неискушённого слушателя звучат безумно похоже. Тут тренькнуло, там звякнуло. Но нас ведь не интересуют неискушённые слушатели, правда? У фанатов Einstürzende Neubauten (к которым я отношусь)) свяо тмоасфреа — вы только послушайте это или это. Большинство этих звуков, тем не менее, получены вживую, а мы говорили о синтезе. Первая мысль у большинства саунддизайнеров при словах "металлический звук" — это мысль про FM-синтез. А FM-синтез — это много сложных модуляций! Например, вот такой простой звук, сделанный на коленке за 5 минут:

Metal Simple by Lakret
выглядит в синтезаторе примерно так:
Ладно, это не особо интересно. А если увеличить атаку и ПИЗДАНУТЬ РЕВЕРБА?))
При желании из этого простенького звука можно легко сделать очень психоделические вещи:
Но интереснее то, что произойдёт с этим звуком, если мы немножко поиграемся с огибающими. Было так:

Стало так:

И звучит это вот так (без БЕЗУМИЯ):
Metal env 1 by Lakret
(с БЕЗУМИЕМ):
Metal env 1b by Lakret
Wow, не правда ли?)
To be continued... 


P.S. Btw, если вам понравилось наше маленькое путешествие, а точнее — звуки, которые получились, вам стоит послушать это.