轨道
/
Julia
Julia
/
练习
/
精品店推荐
精品店推荐

精品店推荐

学习练习

简介

用过 Python 的人几乎都用过列表推导式,从早期版本开始,它一直是 Python 语法的核心特性。

如此方便的东西会逐渐进入其他语言,Julia 也不例外。

对 Julia 程序员来说,推导式是一个选项,而不是必需品,因为通常还有别的做法(广播、高阶函数等)。

不过,推导式往往能提供一种简单、易读且性能不错的构造数组的方式。 用不用它,最终取决于个人口味,以及你更喜欢 Python 还是函数式语言。

语法基本是直接照搬 Python,但为高维数组做了扩展。

单变量

非常有 Python 风格,包括可选的 if 子句。

julia> [x^2 for x in 1:3]
3-element Vector{Int64}:
 1
 4
 9
julia> [x^2 for x in 1:3 if isodd(x^2)]
2-element Vector{Int64}:
 1
 9

输出形状取决于推导式的细节:在简单情况下通常与输入集合相同,但 if 子句会把它压平成一个 Vector。

julia> m
2×3 Matrix{Int64}:
 1  2  3
 4  5  6
julia> [x^2 for x in m]
2×3 Matrix{Int64}:
  1   4   9
 16  25  36
julia> [x^2 for x in m if isodd(x^2)] 
3-element Vector{Int64}:
  1
 25
  9

多变量,Vector 输出

和 Python 一样,我们可以写多个 for 子句,用不同的变量,输入集合可以相同,也可以不同。

julia> [x * y for x in 1:3 for y in 4:6]
9-element Vector{Int64}:
  4
  5
  6
  8
 10
 12
 12
 15
 18
julia> [x * y for x in 1:3 for y in 4:6 if isodd(x * y)]
2-element Vector{Int64}:
  5
 15

这等价于嵌套循环。 即使输入是矩阵,输出也是一维的。

julia> m
2×3 Matrix{Int64}:
 1  2  3
 4  5  6
julia> [x*y for x in m for y in m]
36-element Vector{Int64}:
  1
  4
  2
  5
(truncated output...)

多变量,多维输出

上一节讲的是只用空格分隔的多个 for 子句。

这一节里只有一个 for,变量之间用逗号分隔。

julia> [(x, y) for x in 1:3, y in 4:6]
3×3 Matrix{Tuple{Int64, Int64}}:
 (1, 4)  (1, 5)  (1, 6)
 (2, 4)  (2, 5)  (2, 6)
 (3, 4)  (3, 5)  (3, 6)

推导式中的每个变量都会在输出中创建一个新维度,变量的每一种可能组合对应一个条目。

在上面的例子里,x 沿行方向递增,y 沿列方向递增。 还可以有更高的维度,不过照例要提醒一句:输出的可读性会成问题。

生成器表达式

前几节讲的重点是数组输出,推导式放在方括号 [ ... ] 里。

当方括号换成圆括号 ( ... ) 时,情况就不一样了:我们得到的是一种generator expression,一个惰性求值的迭代器,可以按需产出下一个值。

julia> g = ((x, y) for x in 1:3, y in 4:6)
Base.Generator{Base.Iterators.ProductIterator{Tuple{UnitRange{Int64}, UnitRange{Int64}}}, var"#9#10"}(var"#9#10"(), Base.Iterators.ProductIterator{Tuple{UnitRange{Int64}, UnitRange{Int64}}}((1:3, 4:6)))
# Indexing fails with a generator, the entries don't exist yet
julia> g[1, 2]
ERROR: MethodError: no method matching getindex(::Base.Generator{Base.Iterators.ProductIterator{Tuple{UnitRange{…}, UnitRange{…}}}, var"#9#10"}, ::Int64, ::Int64)
# conversion to array
julia> collect(g)
3×3 Matrix{Tuple{Int64, Int64}}:
 (1, 4)  (1, 5)  (1, 6)
 (2, 4)  (2, 5)  (2, 6)
 (3, 4)  (3, 5)  (3, 6)
# generators are mainly designed for iteration
julia> [i * j for (i, j) in g]
3×3 Matrix{Int64}:
  4   5   6
  8  10  12
 12  15  18

当推导式的结果马上要用于后续处理时,生成器可以节省内存,不必存储一个很大的中间数组。

作为函数参数使用的生成器不需要额外的括号。

# inefficient
julia> v = [x^2 for x in 1:1e6];
julia> sum(v)
3.333338333335e17
# better - use a generator
julia> sum(x^2 for x in 1:1e6)
3.3333383333312755e17

生成器的语法和推导式完全相同,只是外面的括号不同。

生成器在字典构造中也很方便。

julia> Dict(x => x^2 for x in 1:5)
Dict{Int64, Int64} with 5 entries:
  5 => 25
  4 => 16
  2 => 4
  3 => 9
  1 => 1

说明

你在一家在线时尚精品店工作。 你想出了一个网站功能,可以向用户推荐穿搭。 你关注的是各种可能的穿搭、它们的价格,以及排除掉撞色的选项。

Note

解决下面这些问题可能有多种方法,但都可以用推导式来解决。

1. 创建服装单品

实现clothingitem(),它接收两个Tuple:一个包含categories,另一个包含各自对应的qualities。 该函数应返回一个Dict,以categories为键,以qualities为值。

julia> categories = ("item_name", "price", "color", "base_color");

julia> qualities = ("Descriptive Name", 99.00, "Ochre Red", "red");

julia> clothingitem(categories, qualities)
Dict{String, Any} with 4 entries:
  "price"      => 99.0
  "item_name"  => "Descriptive Name"
  "base_color" => "red"
  "color"      => "Ochre Red"

你的函数应能处理任意数量的categories。

2. 推荐组合

实现get_combinations(),它接收一个上衣的Tuple和一个下装的Tuple。 该函数应返回这两个Tuple的cartesian product,形式为二维数组(即Matrix)。 每个元素都是一个(top, bottom)组合,类型为Tuple。 上衣沿行方向变化,下装沿列方向变化。

julia> tops = (
        Dict("item_name" => "Dress shirt"),
        Dict("item_name" => "Casual shirt")
        );

julia> bottoms = (
        Dict("item_name" => "Jeans"),
        Dict("item_name" => "Dress trousers")
       );

julia> get_combinations(tops, bottoms)
2×2 Matrix{Tuple{Dict{String, String}, Dict{String, String}}}:
 (Dict("item_name"=>"Dress shirt"), Dict("item_name"=>"Jeans"))   (Dict("item_name"=>"Dress shirt"), Dict("item_name"=>"Dress trousers"))
 (Dict("item_name"=>"Casual shirt"), Dict("item_name"=>"Jeans"))  (Dict("item_name"=>"Casual shirt"), Dict("item_name"=>"Dress trousers"))

3. 累加整套穿搭的价格

每件衣服都带有一个price字段,因此能够比较整套穿搭的总价会很有帮助。 实现get_prices(),它接收一个服装组合数组。 该函数应返回一个形状相同的数组,各套穿搭的价格位于各自对应的位置。

julia> tops = (
        Dict("item_name" => "Dress shirt", "base_color" => "blue", "price" => 35),
        Dict("item_name" => "Casual shirt", "base_color" => "black", "price" => 20)
       );

julia> bottoms = (
        Dict("item_name" => "Jeans", "base_color" => "blue", "price" => 30),
        Dict("item_name" => "Dress trousers", "base_color" => "black", "price" => 75)
       );

julia> combomatrix = get_combinations(tops, bottoms);

julia> get_prices(combomatrix)
2×2 Matrix{Int}::
 65  110
 50   95

julia> filteredmatrix = filter_clashing(combomatrix)
1-element Vector{Tuple{Dict{String, Any}, Dict{String, Any}}}:
 (Dict("price" => 20, "item_name" => "Casual shirt", "base_color" => "black"), Dict("price" => 30, "item_name" => "Jeans", "base_color" => "blue"))

julia> get_prices(filteredmatrix)
1-element Vector{Int}:
 50

4. 过滤掉撞色的穿搭

每件衣服都有一个base_color字段。 用这个字段移除上衣与下装底色相同的所有组合。 实现filter_clashing(),它接收一个服装组合数组。 返回一个一维数组(即Vector),包含所有符合条件的组合。

julia> tops = (
        Dict("item_name" => "Dress shirt", "base_color" => "blue", "price" => 35),
        Dict("item_name" => "Casual shirt", "base_color" => "black", "price" => 20)
       );

julia> bottoms = (
        Dict("item_name" => "Jeans", "base_color" => "blue", "price" => 30),
        Dict("item_name" => "Dress trousers", "base_color" => "black", "price" => 75)
       );

julia> combos = get_combinations(tops, bottoms);

julia> filter_clashing(combos)
1-element Vector{Tuple{Dict{String, Any}, Dict{String, Any}}}:
 (Dict("price" => 20, "item_name" => "Casual shirt", "base_color" => "black"), Dict("price" => 30, "item_name" => "Jeans", "base_color" => "blue"))
通过 GitHub 编辑 链接将在新窗口或新标签页中打开
Julia Exercism

准备好开始 精品店推荐 了吗?

注册 Exercism,借助 35 个概念128 个练习 和真人导师指导,学习并掌握 Julia,全部免费。