________fear_of_underscores

среда, 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#))

Комментариев нет:

Отправить комментарий