# 命令行中 tree 的多重实现

## 解题思路

1. 利用递归，将目录转换成 `{:name: ".", :children: []}` 结构
2. 对于第一层目录名，前缀装饰成 `T_branch = "├── "`或者 `L_branch = "└── "`
3. 对于子目录，前缀装饰成 `I_branch = "│ "`或者`SPACER = " "` 举例如下：
```.
├── tree.py  # 不是最后一项，所以使用 T_branch 前缀
├── files.py
├── lists.py
├── tuples.py
├── resources
│   └── README.md # 由于其父亲不是最后一项，所以使用 I_branch 前缀
├── recursion.py
└── data    # 是最后一项，所以使用 L_branch 前缀
├── output.txt # 由于其父亲是最后一项，所以使用 SPACE 前缀
└── data.txt```

## python 实现

```#!/usr/local/bin/python
# -*- coding: UTF-8 -*-

"""
list a directory in tree way.

children(path):
map(lambda name: tree(path, name), listdir(path))

tree(parent, dir_name):
if is_file(parent, dir_name):
return {'name': dir_name, 'children': []}
else:
children = children(join(parent, dir_name))
return {'name': dir_name, 'children': children}
"""
import os
import functools as fp

I_branch = "│   "
T_branch = "├── "
L_branch = "└── "
SPACER = "    "

def _children(path):
return map(lambda filename: tree_format(path, filename), os.listdir(path))

def tree_format(parent, dir_name):
path = os.path.join(parent, dir_name)
is_file = os.path.isfile(path)
children = [] if is_file else _children(path)
return {'name': dir_name, 'children': list(children)}

def render_tree(tr):
name = tr['name']
children = tr['children']
return [name] + fp.reduce(lambda l, r: l + r,
map(lambda arg: render(len(children))(*arg),
enumerate(children)),
[])

def render(length):
def prefix(index, child):
is_last = (index == length - 1)
prefix_first = L_branch if is_last else T_branch
prefix_rest = SPACER if is_last else I_branch
tr = render_tree(child)
tail = [prefix_rest + t for t in tr[1:]]
return prefix

if __name__ == "__main__":
print '\n'.join(render_tree(tree('', sys.argv[1])))

\$ python3 tree.py . #打印当前的目录的所有文件及子目录
.
├── tree.py
├── files.py
├── lists.py
├── tuples.py
├── resources
├── recursion.py
└── data
├── output.txt
└── data.txt```

## Clojure 实现

```(ns tree
(:require [clojure.java.io :as io]
[clojure.string :as str]))
(def L-branch "└── ")
(def T-branch "├── ")
(def I-branch "│   ")
(def SPACE    "    ")

(declare tree)

(defn children [path]
(map #(tree %) (.listFiles path)))

(defn tree [dir-name]
(let [path (io/file dir-name)
dir? (.isDirectory path)]
{:name (.getName path)
:children (if dir? (children path))}))

(defn render-tree [{name :name children :children}]
(cons name
(mapcat (fn [child index]
(let [last? (= index (dec (count children)))
prefix-first (if last? L-branch T-branch)
prefix-rest (if last? SPACE I-branch)
sub-tree (render-tree child)]
(cons (str prefix-first (first sub-tree))
(map #(str prefix-rest %) (rest sub-tree)))))
children
(range))))

(defn -main [& args]
(->>
(tree (first args))
(render-tree)
(str/join "\n")
(println)))
\$ lein run -m tree .
.
├── tree.py
├── files.py
├── lists.py
├── tuples.py
├── resources
├── recursion.py
└── data
├── output.txt
└── data.txt```

## Golang 实现

```package main

import (
"fmt"
"io/ioutil"
"os"
"path"
"strings"
)

const (
I_branch = "│   "
T_branch = "├── "
L_branch = "└── "
SPACER   = "    "
)

type entry struct {
name     string
children []entry
}

func (e entry) String() string {
if len(e.children) == 0 {
return e.name
} else {
s := e.name
for _, child := range e.children {
s += child.String()
}

return s
}
}

func Children(path string) []entry {
result := []entry{}
for _, f := range files {
result = append(result, Tree(path, f.Name()))
}

return result
}

func Tree(parent, dirName string) entry {
realPath := path.Join(parent, dirName)
theChildren := []entry{}
if f, ok := os.Stat(realPath); ok == nil {
if f.IsDir() {
theChildren = Children(realPath)
}
}
return entry{name: dirName, children: theChildren}
}

func RenderTree(e entry) []string {
name := e.name
children := e.children
result := []string{name}

for index, child := range children {
subTree := RenderTree(child)
prefixFirst := T_branch
prefixRest := I_branch
if index == len(children)-1 {
prefixFirst = L_branch
prefixRest = SPACER
}

result = append(result, prefixFirst+subTree[0])

for _, sub := range subTree[1:] {
result = append(result, prefixRest+sub)
}
}
return result
}

func main() {
fmt.Println(strings.Join(RenderTree(Tree("", os.Args[1])), "\n"))
}
\$ go run tree.go .
.
├── data
│   ├── data.txt
│   └── output.txt
├── files.py
├── lists.py
├── recursion.py
├── resources
├── tree.py
└── tuples.py```

## NodeJS 实现

```const path = require('path')
const fs = require('fs')
const I_branch = '│   '
const T_branch = '├── '
const L_branch = '└── '
const SPACER   = '    '

function children(path) {
}

function tree(parentDir, dirName) {
let realPath = path.join(parentDir, dirName)
let isDir = fs.statSync(realPath).isDirectory()
return {name: dirName, children: isDir ? children(realPath) : []}
}

function prefix(len) {
return (tr, index) => {
let isLast = len == index + 1
let prefixFirst = isLast ? L_branch : T_branch
let prefixRest = isLast ? SPACER : I_branch

return [prefixFirst + head].concat(tail.map(name => prefixRest + name))
}
}

function renderTree({name: name, children: children}) {
return [name]
.concat(children
.map(prefix(children.length))
.reduce((l, r) => l.concat(r), []))
}

console.log(renderTree(tree('', process.argv[2])).join('\n'))

\$ node tree.js .
.
├── data
│   ├── data.txt
│   └── output.txt
├── files.py
├── lists.py
├── recursion.py
├── resources
├── tree.py
└── tuples.py```

## Kotlin script

```import java.io.File

val I_branch = "│   "
val T_branch = "├── "
val L_branch = "└── "
val SPACER   = "    "

data class Entry (val name: String, val children: List<Entry>)

fun children(path: File): List<Entry> {
return path.listFiles().map {tree(it)}
}

fun tree(path: File): Entry {
val isDir = path.isDirectory()
return Entry(path.getName(), if(isDir) children(path) else listOf<Entry>())
}

fun renderTree(tree: Entry): List<String> {
val name = tree.name
val children = tree.children

return listOf(name) + children.mapIndexed { i, e -> prefix(children.size)(i, e) }.fold(listOf<String>()) {l, r -> l + r}
}

fun prefix(size: Int): (Int, Entry) -> List<String> {
return {index, entry ->
val isLast = index + 1 == size
val prefixFirst = if(isLast) L_branch else T_branch
val prefixRest = if(isLast) SPACER else I_branch
val subTree = renderTree(entry)

listOf(prefixFirst + subTree.first()) + subTree.drop(1).map {t -> prefixRest + t}
}
}

println(renderTree(tree(File(args[0]))).joinToString("\n"))

\$ kotlinc -script tree.kts .
.
├── tree.py
├── files.py
├── lists.py
├── tuples.py
├── resources
├── recursion.py
└── data
├── output.txt
└── data.txt```

## Scala

```import java.io._
val I_branch = "│   "
val T_branch = "├── "
val L_branch = "└── "
val SPACER   = "    "

case class Entry(name: String, children: List[Entry])

def children(path: File): List[Entry] = path.listFiles().toList.map((it: File) => tree(it))

def tree(path: File): Entry = Entry(path.getName(), if(path.isDirectory()) children(path) else List[Entry]())

def prefix(size: Int) = (index: Int, entry: Entry) => {
val isLast = index + 1 == size
val prefixFirst = if(isLast) L_branch else T_branch
val prefixRest = if(isLast) SPACER else I_branch
val subTree = renderTree(entry)
List(prefixFirst + subTree.head) ++ subTree.tail.map(t => prefixRest + t)
}

def renderTree(tree: Entry): List[String] = {
val name = tree.name
val children = tree.children

return List(name) ++ children
.zipWithIndex
.map({case (e: Entry, i: Int) => prefix(children.size)(i, e)})
.fold(List[String]())((l, r) => l ++ r)
}

println(renderTree(tree(new File(args(0)))).mkString("\n"))

\$ scala tree.scala .
.
├── tree.py
├── files.py
├── lists.py
├── tuples.py
├── resources
├── recursion.py
└── data
├── output.txt
└── data.txt```

## Elixir

```#!/usr/bin/env elixir

defmodule Tree do
def main([dir | _]) do
dir |> tree_format |> render_tree |> Enum.join("\n") |> IO.puts
end

defp children(path) do
if (path |> File.dir?) do
File.ls!(path) |> Enum.map(fn f -> tree_format(path, f) end)
else
[]
end
end

defp tree_format(parent_dir \\ ".", dir_name) do
%{:name => dir_name, :children => Path.join(parent_dir, dir_name) |> children}
end

defp decorate(is_last?, [parent | children]) do
prefix_first = (if (is_last?), do: "└── ", else: "├── ")
prefix_rest = (if (is_last?), do: "    ", else: "│   ")
[prefix_first <> parent | children |> Enum.map(fn child -> prefix_rest <> child end)]
end

defp render_tree(%{name: dir_name, children: children}) do
[dir_name
| children
|> Enum.with_index(1)
|> Enum.map(fn {child, index} -> decorate(length(children) == index, render_tree(child)) end)
|> List.flatten]
end

end

Tree.main(System.argv)

\$ elixir tree.exs .
.
├── tree.py
├── files.py
├── lists.py
├── tuples.py
├── resources
├── recursion.py
└── data
├── output.txt
└── data.txt```

0 条评论

• ### 架构整洁之道导读（三）

上回说到组件聚合，反映的是组件内部的“基本元素”的选择标准。第14章介绍的组件耦合则是指组件和组件之间的关系，这些依赖关系有些是好的，有些是不好的，我们即将看到...

• ### Rust 入门 (Rust Rocks)

做区块链的基本几乎没有人不知道 Rust 这门编程语言，它非常受区块链底层开发人员的青睐。说来也奇怪，Rust 起源于 Mazilla，唯一大规模应用就是 Fi...

• ### 泛型编程

泛型编程是一种编程风格，其中算法以尽可能抽象的方式编写，而不依赖于将在其上执行这些算法的数据形式。

• ### python练习题-day3

16）从name变量对应的值中找到"N"对应的索引(如果找不到则报错)，并输出结果

• ### C语言程序可以没有main函数

学习C语言的同学都知道,每个C程序要有一个main函数,程序从main函数开始执行,在main函数中结束。但事实上,C程序也可以没有main函数,或者说自己可以...

• ### 普通学渣的春招，秋招历程以及实习心路

版权声明：本文为博主原创文章，未经博主允许不得转载。 https://blog.csdn.net/sinat_35512245/articl...

• ### Caffe2 - (六)CPU/GPU 模式切换

在尝试把 Caffe 模型转换到 Caffe2，部署时 CPU/GPU 模式切换方法找了很久才找到一个用着可以，记录下.

• ### Perl 工作积累（不定期更新）

学会用 perl -c 检查语法， perl -e 直接执行语句， perldoc 查看文档 ， CPAN 查找 module ... # 注释； =pod ...

hadoop2.7.1+ubuntu 14.04 hive 2.0.1 集群环境 namenode节点：master (hive服务端) datanode 节点...