記一次遞迴在我專案中所發揮的作用

陳大魚頭發表於2018-12-29

我的github地址:Github

背景

在最近的專案中,有這麼一個功能點,就是要獲取在WEB IDE裡使用者所寫的註釋中的一段特殊規則,然後解析成一段JS config 物件
例如:

//% width="100px" height="200px"//% pos.top="50px" pos.left="50px"//% writable=true//% q.b.d.w.r.f=30 q.b.d.w.r.a=40複製程式碼

要轉成

{ 
width: '100px', height: '200px', pos: {
top: '50px', left: '50px'
}, writable: true, q: {
b: {
d: {
w: {
r: {
f: 30, a: 40
}
}
}
}
}
}複製程式碼

類似的規則

悲傷蛙

什麼是遞迴

來自百度的解釋:
遞迴:程式呼叫自身的程式設計技巧稱為遞迴 (recursion)
尾遞迴:如果一個函式中所有遞迴形式的呼叫都出現在函式的末尾,我們稱這個遞迴函式是尾遞迴的。

就是 復讀機

復讀機

遞迴怎麼寫?

一般

const fibonacci = num =>
(num === 1 ? 1 : num * fibonacci(num - 1))複製程式碼

尾遞迴

const fibonacci = (num, total = 1) =>
(num === 0 ? total : fibonacci(num - 1, num * total))複製程式碼

Array.reduce

const getArray = count =>
Array.from({
length: count
}, (value, key) =>
key)const fibonacci = num =>
getArray(num).reduceRight((accumulator, currentValue) =>
accumulator * currentValue)複製程式碼

功能實現

分步實現

  1. 過濾常規內容,獲取特殊的備註資訊,去除空格,並且轉成陣列
    • 此時的陣列內容為
      [    '//% blockId="sloth_servo_write"  block="set servo %channel|degree %degree"',    '//% advanced=true',    '//% weight=50',    '//% degree.min=0 degree.max=180',    '//% channel.fieldEditor="gridpicker" channel.fieldOptions.columns=4',    '//% a.b.c.d=20 a.b.c.e=222',    '//% q.b.d.w.r.f=30 q.b.d.w.r.a=40']複製程式碼
    const code = `//% width="100px" height="200px"//% pos.top="50px" pos.left="50px"//% writable=true//% q.b.d.w.r.f=30 q.b.d.w.r.a=40`// 獲取特殊註釋陣列const annotation_array_filter = annotation_item =>
    annotation_item.indexOf('//%') >
    = 0;
    // 去除特殊註釋前後的空格const annotation_array_remove_space = annotation_item =>
    annotation_item.trim();
    const annotation_array = code.split('\n') .filter(annotation_array_filter) .map(annotation_array_remove_space)複製程式碼
  2. 遍歷特殊規則陣列,把每一項配置都壓入一個新物件
    • 此時的新物件內內容為
      { 
      a.b.c.d: 20, a.b.c.e: 222, advanced: true, block: "set servo %channel|degree %degree", blockId: "sloth_servo_write", channel.fieldEditor: "gridpicker", channel.fieldOptions.columns: 4, degree.max: 180, degree.min: 0, q.b.d.w.r.a: 40, q.b.d.w.r.f: 30, weight: 50,
      }複製程式碼
      const annotation_array_loop = annotation_item =>
{
// 把註釋中的每一項配置轉成物件 const result_forEach = result_item =>
{
let annotation_sub_object = {
};
// 如果特殊註釋陣列中的每一項包含多個配置,則扁平化 const array_flattened = data =>
{
const is_array = (this.type(data) === '[object Array]');
const object_recursion = () =>
{
const [key, value] = data.split('=');
const annotation_sub_object = {
};
try {
annotation_sub_object[key] = JSON.parse(value);

} catch (error) {
annotation_sub_object[key] = JSON.parse(value + '"')
};
annotation_object = {
...annotation_object, ...annotation_sub_object
};

};
// 判斷註釋陣列項中每一個元素是否有多個配置,如果有則遞迴,否則則注入物件 is_array ? data.forEach(e =>
{
array_flattened(e);

}) : object_recursion();

};
array_flattened(result_item);

};
// 去除特殊陣列中每一項多餘的內容 const result_map = result_item =>
(result_item.match(/\=/g).length >
1 ? result_item.split(' ') : result_item);
const result = annotation_item.replace('//% ', '') .split('/\" /g') .map(result_map);
result_forEach(result);

};
let annotation_object = {
};
// 承載每一個配置的物件 annotation_array.forEach(annotation_array_loop);
複製程式碼
  1. 把陣列裡的元素轉成物件
    • 此時陣列內容為
      [    { 
      blockId: "sloth_servo_write"
      }, {
      advanced: true
      }, ...]複製程式碼
      let main_array = [];
// 承載每一個配置的陣列 const annotation_object_keys = Object.keys(annotation_object);
// 獲取扁平化後的註釋物件的key const annotation_object_keys_loop = annotation_object_key =>
{
// 迴圈變數每一項註釋 const annotation_object_key_array = annotation_object_key.split('.');
// 把多級物件轉成陣列 const annotation_object_value = annotation_object[annotation_object_key];
// 獲取每一項元素的值 let sub_object = {
};
// 暫時承載配置物件的物件 const key_reduce = (accumulator, current_value, current_index, array) =>
{
// key值遞迴,對每一項配置進行合併 if (current_index === 0) {
// 如果當前遍歷的元素為第一項,也就是說為配置的頂級物件,所以直接壓入物件,並且輸出 sub_object[current_value] = (current_index === array.length - 1 ? annotation_object_value : {
});
return sub_object[current_value];

} accumulator[current_value] = {
};
// 如果當前遍歷的元素不為第一項,則當前物件元素變為物件 if (current_index === array.length - 1) {
// 如果當前遍歷的元素為陣列最後一項,說明是配置物件最底的元素,可以直接賦值 accumulator[current_value] = annotation_object_value;

} return accumulator[current_value];

};
let level_object = annotation_object_key_array.reduce(key_reduce, annotation_object_key_array[0]);
level_object = undefined;
// 清空level_object main_array.push(sub_object);
sub_object = undefined;
// 清空sub_object
} annotation_object_keys.forEach(annotation_object_keys_loop);
複製程式碼
  1. 遞迴合併物件
    • 此時的物件為
      { 
      a: {b: {…
      }
      }, advanced: true, block: "set servo %channel|degree %degree", blockId: "sloth_servo_write", channel: {fieldEditor: "gridpicker", fieldOptions: {…
      }
      }, degree: {min: 0, max: 180
      }, q: {b: {…
      }
      }, weight: 50
      }複製程式碼
      const annotation_tree = {
};
const tree_data = (key, value, object) =>
{
// 遞迴合併物件 if (this.type(value) !== '[object Object]') {
// 如果當前傳入元素為物件,則直接壓入物件中 object[key] = value;

} else {
// 否則繼續遞迴 if (!object[key]) {
object[key] = {
};

};
for (let item in value) {
tree_data(item, value[item], object[key]);

}
};

};
const main_array_forEach = item =>
{
// 迴圈遍歷配置陣列 const key = Object.keys(item)[0];
const value = Object.values(item)[0];
tree_data(key, value, annotation_tree);

};
main_array.forEach(main_array_forEach);
main_array = undefined;
// 清空main_array複製程式碼

完整程式碼

// 程式碼轉換器((wid, dcm) =>
{
'use strict';
const win = wid;
const doc = dcm;
// 基礎資訊 const base_info = {
'version': '0.0.1', 'author': 'kris',
};
// 輸出的函式 const funcs = {
annotation_parser (annotation) {
// 配置樹初始化 this.annotation_tree = {
};
// 獲取特殊註釋陣列 const annotation_array_filter = annotation_item =>
annotation_item.indexOf('//%') >
= 0;
// 去除特殊註釋前後的空格 const annotation_array_remove_space = annotation_item =>
annotation_item.trim();
// 迴圈遍歷特殊註釋陣列 const annotation_array_loop = annotation_item =>
{
// 把註釋中的每一項配置轉成物件 const result_forEach = result_item =>
{
let annotation_sub_object = {
};
// 如果特殊註釋陣列中的每一項包含多個配置,則扁平化 const array_flattened = data =>
{
const is_array = (this.type(data) === '[object Array]');
const object_recursion = () =>
{
const [key, value] = data.split('=');
const annotation_sub_object = {
};
try {
annotation_sub_object[key] = JSON.parse(value);

} catch (error) {
annotation_sub_object[key] = JSON.parse(value + '"')
};
annotation_object = {
...annotation_object, ...annotation_sub_object
};

};
// 判斷註釋陣列項中每一個元素是否有多個配置,如果有則遞迴,否則則注入物件 is_array ? data.forEach(e =>
{
array_flattened(e);

}) : object_recursion();

};
array_flattened(result_item);

};
// 去除特殊陣列中每一項多餘的內容 const result_map = result_item =>
(result_item.match(/\=/g).length >
1 ? result_item.split(' ') : result_item);
const result = annotation_item.replace('//% ', '') .split('/\" /g') .map(result_map);
result_forEach(result);

};
let annotation_object = {
};
// 承載每一個配置的物件 annotation.filter(annotation_array_filter) .map(annotation_array_remove_space) .forEach(annotation_array_loop);
let main_array = [];
// 承載每一個配置的陣列 const annotation_object_keys = Object.keys(annotation_object);
// 獲取扁平化後的註釋物件的key const annotation_object_keys_loop = annotation_object_key =>
{
// 迴圈變數每一項註釋 const annotation_object_key_array = annotation_object_key.split('.');
// 把多級物件轉成陣列 const annotation_object_value = annotation_object[annotation_object_key];
// 獲取每一項元素的值 let sub_object = {
};
// 暫時承載配置物件的物件 const key_reduce = (accumulator, current_value, current_index, array) =>
{
// key值遞迴,對每一項配置進行合併 if (current_index === 0) {
// 如果當前遍歷的元素為第一項,也就是說為配置的頂級物件,所以直接壓入物件,並且輸出 sub_object[current_value] = (current_index === array.length - 1 ? annotation_object_value : {
});
return sub_object[current_value];

} accumulator[current_value] = {
};
// 如果當前遍歷的元素不為第一項,則當前物件元素變為物件 if (current_index === array.length - 1) {
// 如果當前遍歷的元素為陣列最後一項,說明是配置物件最底的元素,可以直接賦值 accumulator[current_value] = annotation_object_value;

} return accumulator[current_value];

};
let level_object = annotation_object_key_array.reduce(key_reduce, annotation_object_key_array[0]);
level_object = undefined;
// 清空level_object main_array.push(sub_object);
sub_object = undefined;
// 清空sub_object
} annotation_object_keys.forEach(annotation_object_keys_loop);
const tree_data = (key, value, object) =>
{
// 遞迴合併物件 if (this.type(value) !== '[object Object]') {
// 如果當前傳入元素為物件,則直接壓入物件中 object[key] = value;

} else {
// 否則繼續遞迴 if (!object[key]) {
object[key] = {
};

};
for (let item in value) {
tree_data(item, value[item], object[key]);

}
};

};
const main_array_forEach = item =>
{
// 迴圈遍歷配置陣列 const key = Object.keys(item)[0];
const value = Object.values(item)[0];
tree_data(key, value, this.annotation_tree);

};
main_array.forEach(main_array_forEach);
main_array = undefined;
// 清空main_array
},
};
// 引用的資源 const libs = {
};
// 工具函式 const tools = {
// 獲取元素型別 type (object) {
return Object.prototype.toString.call(object);

}, // 分離傳入的程式碼跟配置 separate_code_and_config (data) {
data.split('\n') .forEach(item =>
{
item.indexOf('//%') >
= 0 ? this.blockly_config_array.push(item.trim()) : this.python_code_array.push(item);

});

},
};
// 定義的元素 const vars = {
blockly_config_array: [], python_code_array: [], annotation_tree: {
}, python_tree: {
},
};
// 根物件 const code_transformer = {
...base_info, ...libs, ...funcs, ...tools, ...vars,
};
const _global = (() =>
{
return this || (0, eval)('this');

})();
if (typeof module !== 'undefined' &
&
module.exports) {
module.exports = code_transformer;

} else if (typeof define === 'function' &
&
define.amd) {
define([], function () {
return code_transformer;

});

} else {
!('code_transformer' in _global) &
&
(_global.code_transformer = code_transformer);

};

})(window, document);
複製程式碼

備註:函式體積好大呀,但這只是業務裡的一個小小小功能,流下了不會優化程式碼的淚水~

哭

來源:https://juejin.im/post/5c26c764e51d457457291fae

相關文章