Skip to content

Commit 72e05d7

Browse files
author
Andrew
authored
Merge pull request #3 from LukeMathWalker/refactoring-smarter-debugging
A different approach
2 parents b535f28 + 83c9f08 commit 72e05d7

File tree

1 file changed

+96
-118
lines changed

1 file changed

+96
-118
lines changed

src/arrayformat.rs

+96-118
Original file line numberDiff line numberDiff line change
@@ -8,141 +8,116 @@
88
use std::fmt;
99
use super::{
1010
ArrayBase,
11+
Axis,
1112
Data,
1213
Dimension,
1314
NdProducer,
1415
Ix
1516
};
16-
use crate::dimension::IntoDimension;
17+
use crate::aliases::Ix1;
1718

1819
const PRINT_ELEMENTS_LIMIT: Ix = 3;
1920

20-
fn get_overflow_axes(shape: &[Ix], limit: usize) -> Vec<usize> {
21-
shape.iter()
22-
.enumerate()
23-
.rev()
24-
.filter(|(_, axis_size)| **axis_size > 2 * limit)
25-
.map(|(axis, _)| axis)
26-
.collect()
21+
fn format_1d_array<A, S, F>(
22+
view: &ArrayBase<S, Ix1>,
23+
f: &mut fmt::Formatter,
24+
mut format: F,
25+
limit: Ix) -> fmt::Result
26+
where
27+
F: FnMut(&A, &mut fmt::Formatter) -> fmt::Result,
28+
S: Data<Elem=A>,
29+
{
30+
let to_be_printed = to_be_printed(view.len(), limit);
31+
32+
let n_to_be_printed = to_be_printed.len();
33+
34+
write!(f, "[")?;
35+
for (j, index) in to_be_printed.into_iter().enumerate() {
36+
match index {
37+
PrintableCell::ElementIndex(i) => {
38+
format(&view[i], f)?;
39+
if j != n_to_be_printed - 1 {
40+
write!(f, ", ")?;
41+
}
42+
},
43+
PrintableCell::Ellipses => write!(f, "..., ")?,
44+
}
45+
}
46+
write!(f, "]")?;
47+
Ok(())
2748
}
2849

29-
fn get_highest_axis_to_skip(overflow_axes: &Vec<usize>,
30-
shape: &[Ix],
31-
index: &[Ix],
32-
limit: &usize) -> Option<usize> {
33-
overflow_axes.iter()
34-
.filter(|axis| {
35-
if **axis == shape.len() - 1 {
36-
return false
37-
};
38-
let sa_idx_max = shape.iter().skip(**axis).next().unwrap();
39-
let sa_idx_val = index.iter().skip(**axis).next().unwrap();
40-
sa_idx_val >= limit && sa_idx_val < &(sa_idx_max - limit)
41-
})
42-
.min()
43-
.map(|v| *v)
50+
enum PrintableCell {
51+
ElementIndex(usize),
52+
Ellipses,
4453
}
4554

46-
fn get_highest_changed_axis(index: &[Ix], prev_index: &[Ix]) -> Option<usize> {
47-
index.iter()
48-
.take(index.len() - 1)
49-
.zip(prev_index.iter())
50-
.enumerate()
51-
.filter(|(_, (a, b))| a != b)
52-
.map(|(i, _)| i)
53-
.next()
55+
// Returns what indexes should be printed for a certain axis.
56+
// If the axis is longer than 2 * limit, a `Ellipses` is inserted
57+
// where indexes are being omitted.
58+
fn to_be_printed(length: usize, limit: usize) -> Vec<PrintableCell> {
59+
if length <= 2 * limit {
60+
(0..length).map(|x| PrintableCell::ElementIndex(x)).collect()
61+
} else {
62+
let mut v: Vec<PrintableCell> = (0..limit).map(|x| PrintableCell::ElementIndex(x)).collect();
63+
v.push(PrintableCell::Ellipses);
64+
v.extend((length-limit..length).map(|x| PrintableCell::ElementIndex(x)));
65+
v
66+
}
5467
}
5568

56-
fn format_array<A, S, D, F>(view: &ArrayBase<S, D>,
57-
f: &mut fmt::Formatter,
58-
mut format: F,
59-
limit: Ix) -> fmt::Result
60-
where F: FnMut(&A, &mut fmt::Formatter) -> fmt::Result,
61-
D: Dimension,
62-
S: Data<Elem=A>,
69+
fn format_array<A, S, D, F>(
70+
view: &ArrayBase<S, D>,
71+
f: &mut fmt::Formatter,
72+
mut format: F,
73+
limit: Ix) -> fmt::Result
74+
where
75+
F: FnMut(&A, &mut fmt::Formatter) -> fmt::Result + Clone,
76+
D: Dimension,
77+
S: Data<Elem=A>,
6378
{
64-
if view.shape().is_empty() {
65-
// Handle 0-dimensional array case first
66-
return format(view.iter().next().unwrap(), f)
79+
// If any of the axes has 0 length, we return the same empty array representation
80+
// e.g. [[]] for 2-d arrays
81+
if view.shape().iter().any(|&x| x == 0) {
82+
write!(f, "{}{}", "[".repeat(view.ndim()), "]".repeat(view.ndim()))?;
83+
return Ok(())
6784
}
68-
69-
let overflow_axes: Vec<Ix> = get_overflow_axes(view.shape(), limit);
70-
71-
let ndim = view.ndim();
72-
let nth_idx_max = view.shape()[ndim-1];
73-
74-
// None will be an empty iter.
75-
let mut last_index = match view.dim().into_dimension().first_index() {
76-
None => view.dim().into_dimension().clone(),
77-
Some(ix) => ix,
78-
};
79-
write!(f, "{}", "[".repeat(ndim))?;
80-
// Shows if ellipses for horizontal split were printed.
81-
let mut printed_ellipses_h = vec![false; ndim];
82-
// Shows if the row was printed for the first time after horizontal split.
83-
let mut no_rows_after_skip_yet = false;
84-
85-
// Simply use the indexed iterator, and take the index wraparounds
86-
// as cues for when to add []'s and how many to add.
87-
for (index, elt) in view.indexed_iter() {
88-
let index = index.into_dimension();
89-
90-
let skip_row_for_axis = get_highest_axis_to_skip(
91-
&overflow_axes,
92-
view.shape(),
93-
index.slice(),
94-
&limit
95-
);
96-
if skip_row_for_axis.is_some() {
97-
no_rows_after_skip_yet = true;
98-
}
99-
100-
let max_changed_idx = get_highest_changed_axis(index.slice(), last_index.slice());
101-
if let Some(i) = max_changed_idx {
102-
printed_ellipses_h.iter_mut().skip(i + 1).for_each(|e| { *e = false; });
103-
104-
if skip_row_for_axis.is_none() {
105-
// New row.
106-
// # of ['s needed
107-
let n = ndim - i - 1;
108-
if !no_rows_after_skip_yet {
109-
write!(f, "{}", "]".repeat(n))?;
110-
writeln!(f, ",")?;
85+
match view.shape() {
86+
// If it's 0 dimensional, we just print out the scalar
87+
[] => format(view.iter().next().unwrap(), f)?,
88+
// We delegate 1-dimensional arrays to a specialized function
89+
[_] => format_1d_array(&view.view().into_dimensionality::<Ix1>().unwrap(), f, format, limit)?,
90+
// For n-dimensional arrays, we proceed recursively
91+
shape => {
92+
// Cast into a dynamically dimensioned view
93+
// This is required to be able to use `index_axis`
94+
let view = view.view().into_dyn();
95+
// We start by checking what indexes from the first axis should be printed
96+
// We put a `None` in the middle if we are omitting elements
97+
let to_be_printed = to_be_printed(shape[0], limit);
98+
99+
let n_to_be_printed = to_be_printed.len();
100+
101+
write!(f, "[")?;
102+
for (j, index) in to_be_printed.into_iter().enumerate() {
103+
match index {
104+
PrintableCell::ElementIndex(i) => {
105+
// Proceed recursively with the (n-1)-dimensional slice
106+
format_array(
107+
&view.index_axis(Axis(0), i), f, format.clone(), limit
108+
)?;
109+
// We need to add a separator after each slice,
110+
// apart from the last one
111+
if j != n_to_be_printed - 1 {
112+
write!(f, ",\n ")?
113+
}
114+
},
115+
PrintableCell::Ellipses => write!(f, "...,\n ")?
111116
}
112-
no_rows_after_skip_yet = false;
113-
write!(f, "{}", " ".repeat(ndim - n))?;
114-
write!(f, "{}", "[".repeat(n))?;
115-
} else if !printed_ellipses_h[skip_row_for_axis.unwrap()] {
116-
let ax = skip_row_for_axis.unwrap();
117-
let n = ndim - i - 1;
118-
write!(f, "{}", "]".repeat(n))?;
119-
writeln!(f, ",")?;
120-
write!(f, "{}", " ".repeat(ax + 1))?;
121-
writeln!(f, "...,")?;
122-
printed_ellipses_h[ax] = true;
123-
}
124-
last_index = index.clone();
125-
}
126-
127-
if skip_row_for_axis.is_none() {
128-
let nth_idx_op = index.slice().iter().last();
129-
if overflow_axes.contains(&(ndim - 1)) {
130-
let nth_idx_val = nth_idx_op.unwrap();
131-
if nth_idx_val >= &limit && nth_idx_val < &(nth_idx_max - &limit) {
132-
if nth_idx_val == &limit {
133-
write!(f, ", ...")?;
134-
}
135-
continue;
136-
}
137-
}
138-
139-
if max_changed_idx.is_none() && !index.slice().iter().all(|x| *x == 0) {
140-
write!(f, ", ")?;
141117
}
142-
format(elt, f)?;
118+
write!(f, "]")?;
143119
}
144120
}
145-
write!(f, "{}", "]".repeat(ndim))?;
146121
Ok(())
147122
}
148123

@@ -240,15 +215,17 @@ mod formatting_with_omit {
240215
let a: Array2<u32> = arr2(&[[], []]);
241216
let actual_output = format!("{}", a);
242217
let expected_output = String::from("[[]]");
243-
assert_eq!(actual_output, expected_output);
218+
print_output_diff(&expected_output, &actual_output);
219+
assert_eq!(expected_output, actual_output);
244220
}
245221

246222
#[test]
247223
fn zero_length_axes() {
248224
let a = Array3::<f32>::zeros((3, 0, 4));
249225
let actual_output = format!("{}", a);
250226
let expected_output = String::from("[[[]]]");
251-
assert_eq!(actual_output, expected_output);
227+
print_output_diff(&expected_output, &actual_output);
228+
assert_eq!(expected_output, actual_output);
252229
}
253230

254231
#[test]
@@ -257,7 +234,8 @@ mod formatting_with_omit {
257234
let a = arr0(element);
258235
let actual_output = format!("{}", a);
259236
let expected_output = format!("{}", element);
260-
assert_eq!(actual_output, expected_output);
237+
print_output_diff(&expected_output, &actual_output);
238+
assert_eq!(expected_output, actual_output);
261239
}
262240

263241
#[test]

0 commit comments

Comments
 (0)