8

私はRamda/Folktaleを見始めました。私はディレクトリから来るタスクの配列をマップしようとする際に問題があります。私はファイルの内容を解析しようとしています。Javascriptでタスクの配列にマッピングする

var fs = require('fs'); 
var util = require('util'); 
var R = require('ramda'); 
var Task = require('data.task'); 

var compose = R.compose; 
var map = R.map; 
var chain = R.chain; 


function parseFile(data) { 
     console.log("Name: " + data.match(/\$name:(.*)/)[1]); 
     console.log("Description: " + data.match(/\$description:(.*)/)[1]); 
     console.log("Example path: " + data.match(/\$example:(.*)/)[1]); 
} 

// String => Task [String] 
function readDirectories(path) { 
    return new Task(function(reject, resolve) { 
     fs.readdir(path, function(err, files) { 
      err ? reject(err) : resolve(files); 
     }) 
    }) 
} 

// String => Task String 
function readFile(file) { 
    return new Task(function(reject, resolve) { 
     fs.readFile('./src/less/' + file, 'utf8', function(err, data) { 
      err ? reject(err) : resolve(data); 
     }) 
    }) 
} 

var app = compose(chain(readFile), readDirectories); 

app('./src/less').fork(
    function(error) { throw error }, 
    function(data) { util.log(data) } 
); 

私はディレクトリ内のファイルを読み取り、タスクを返しています。これが解決すると、新しいタスクを返すreadFile関数に入るはずです。ファイルを読み込んだら、そこからいくつかのビットを解析するだけです。以下では

var app = compose(chain(readFile), readDirectories); 

それはreadFileの関数になりますが、「ファイル」はエラーので、ファイルの配列です。で

var app = compose(chain(map(readFile)), readDirectories); 

我々はfs.readfile()に入ることはありませんが、 'ファイル' は、実際のファイル名です。

私はこれについてかなり困惑しており、ドキュメントは困惑しています。どんな提案も大歓迎です。私が理解し、ネストされたマップを容易に型シグネチャを含めることができるので、私は別のライン上の変換のそれぞれを書いた

おかげ

答えて

9
'use strict'; 

const fs = require('fs'); 

const Task = require('data.task'); 
const R = require('ramda'); 


// parseFile :: String -> { name :: String 
//       , description :: String 
//       , example :: String } 
const parseFile = data => ({ 
    name:   R.nth(1, R.match(/[$]name:(.*)/, data)), 
    description: R.nth(1, R.match(/[$]description:(.*)/, data)), 
    example:  R.nth(1, R.match(/[$]example:(.*)/, data)), 
}); 

// readDirectories :: String -> Task (Array String) 
const readDirectories = path => 
    new Task((reject, resolve) => { 
    fs.readdir(path, (err, filenames) => { 
     err == null ? resolve(filenames) : reject(err); 
    }) 
    }); 

// readFile :: String -> Task String 
const readFile = filename => 
    new Task(function(reject, resolve) { 
    fs.readFile('./src/less/' + filename, 'utf8', (err, data) => { 
     err == null ? resolve(data) : reject(err); 
    }) 
    }); 

// dirs :: Task (Array String) 
const dirs = readDirectories('./src/less'); 

// files :: Task (Array (Task String)) 
const files = R.map(R.map(readFile), dirs); 

// sequenced :: Task (Task (Array String)) 
const sequenced = R.map(R.sequence(Task.of), files); 

// unnested :: Task (Array String) 
const unnested = R.unnest(sequenced); 

// parsed :: Task (Array { name :: String 
//       , description :: String 
//       , example :: String }) 
const parsed = R.map(R.map(parseFile), unnested); 

parsed.fork(err => { 
       process.stderr.write(err.message); 
       process.exit(1); 
      }, 
      data => { 
       process.stdout.write(R.toString(data)); 
       process.exit(0); 
      }); 

。これらはもちろんR.pipe経由でパイプラインに組み込むことができます。

最も興味深いの手順はTask (Array String)Array (Task String)を変換するためにR.sequenceを使用して、そしてTask (Array String)Task (Task (Array String))を変換するためにR.unnestを使用しています。

plaid/async-problemをまだお持ちでない場合は、こちらをご覧ください。

+1

ありがとうございました。それは実際には本当に面白いです。私はそれに夢中になっていたと思っているような変化です。ブールブックスの書籍に戻る。 – SpaceBeers

+0

私はラムダ文書で通勤を見つけることができません。私は何が欠けていますか? – akaphenom

+0

(私はドキュメンテーションを見つけることができないので)通勤は "control.monads"シーケンス関数とどう違うのですか? – akaphenom

4

デビッドが提案したように、commuteは、いくつかのアプリケーションのリスト(例えばTask)を値のリストを含む単一のアプリケーションに変換するのに便利です。

var app = compose(chain(map(readFile)), readDirectories);

我々は)(fs.readfileに入ることはありませんが、 'ファイル' を実際のファイル名です。それは上記のコードを意味し、別のmapステップの世話をするよう密接に関連commuteMapがここにも役立ちます

ものように表すことができるはずです

var app = compose(chain(commuteMap(readFile, Task.of)), readDirectories); 
+0

それは素晴らしいです。ありがとう。 – SpaceBeers

0

私は同様の問題がありましたディレクトリ内のすべてのファイルを読み込み、RAMDAのpipePを開始しました:

'use strict'; 

const fs = require('fs'); 
const promisify = require("es6-promisify"); 
const _ = require('ramda'); 

const path = './src/less/'; 
const log = function(x){ console.dir(x); return x }; 
const map = _.map; 
const take = _.take; 

const readdir = promisify(fs.readdir); 
const readFile = _.curry(fs.readFile); 
const readTextFile = readFile(_.__, 'utf8'); 
const readTextFileP = promisify(readTextFile); 

var app = _.pipeP(
    readdir, 
    take(2), // for debugging, so don’t waste time reading all the files 
    map(_.concat(path)), 
    map(readTextFileP), 
    promiseAll, 
    map(take(50)), 
    log 
); 

function promiseAll(arr) { 
    return Promise.all(arr) 
} 

app(path); 

Promise.allがpipePが値またはAPを期待して、ファイルを読み込む際に必要としているように見えますロマンス、しかし、ファイルを読む約束の配列を受信して​​います。私が困惑する理由は、関数をインライン化する代わりにPromise.allを返すようにしなければならなかった理由です。

エラーハンドリングが組み込まれているので、タスク/フォークの使い方が面白いです。pipePにcatchブロックを持たせてもらいたいのですが、私のような初心者にとっては難しいかもしれません。

関連する問題