Выражение рекурсии в LINQ

Кажется, что содержимое внутри гибкого div влияет на его расчетный размер относительно свойства flex-grow. Я делаю что-то неправильно?

blockquote>

Источником вашей проблемы является не содержимое внутри элемента flex.

В приведенной ниже скрипте вы увидите цифровую панель. Все строки содержат 3 номера, кроме нижней строки. Эта строка должна иметь «0» - это ширина из двух чисел, поэтому flex-grow: 2 и «:» должны быть размером 1, следовательно flex-grow: 1. Я что-то упустил?

blockquote>

Да. Ваша интерпретация свойства flex-grow неверна. flex-grow не предназначен для определения размера элемента гибкости. Его задача состоит в том, чтобы распределить свободное пространство в контейнере гибких контейнеров между элементами flex.

Применяя flex-grow: 1 к группе элементов гибкости, вы говорите им распределять свободное пространство равномерно между собой. Вот почему в вашей демонстрации строки 1, 2 и 3 имеют одинаковые размеры элементов гибкости.

Когда вы применяете flex-grow: 2, вы указываете, что элемент гибкости потребляет вдвое больше свободного места как элементы с flex-grow: 1. Это не обязательно означает, что элемент flex будет в два раза больше.

Кроме того, в вашем случае, даже если flex-grow: 2 означает в два раза больше размера, как бы он знал, чтобы включить этот второй 10px margin из строк выше в его вычисление ширины (что было бы необходимо для достижения выравнивание, которое вы хотите)?

Причина, по которой выравнивание отключена в строке четвертая, состоит в том, что она имеет только один маркер в 10 пикселей.

Вы заметите, что если вы удалите правило поля , вы получите желаемое выравнивание.

.numbers {
    display: flex;
    flex-direction: column;
}

.row {
    display: flex;
    flex-direction: row;
    flex-grow: 1;
    justify-content: space-between;
}

.button {
    display: flex;
    flex-grow: 1;
    justify-content: center;
    align-items: center;
    /* margin: 5px; */
    border-radius: 5px;
    border: 1px solid gray;
    background: rgba(255, 255, 255, 0.2);
    cursor: pointer;
}

.button#number0 {
    flex-grow: 2;
}

.button#colon {
    flex-grow: 1;
}
1
2
3
4
5
6
7
8
9
0
:

Итак, что происходит на четвертой строке с этим вторым краем 10px?

Он поглощается двумя элементами гибкости.

Вот как flex-grow распределяет дополнительное пространство в строке четвертое:

  • Элемент Flex слева (с содержимым «0») имеет flex-grow: 2. (.button#number0 в коде.)
  • Элемент элемента Flex (с содержимым «:») имеет flex-grow: 1. (.button#colon в коде.)
  • Второе поле между элементами, которое появляется только в строках с тремя элементами гибкости, имеет ширину 10 пикселей. (Код говорит 5px вокруг каждого элемента, но в CSS горизонтальные поля никогда не сжимаются . Кроме того, в flexbox никакие поля не сжимаются .)
  • Сумма общее значение flex-grow равно трем. Итак, давайте разделим 10px на 3. Теперь мы знаем, что доля 1 равна 3.33px.
  • Следовательно, левый элемент flex получает 6.66px дополнительного пространства, а элемент flex item получает 3.33px.
  • Предположим, что на левой стороне было отмечено flex-grow: 3. Затем гибкий элемент слева получит 7.5px, а flex item right получит 2.5px.
blockquote>

В последней части вашего вопроса говорится:

Правая сторона «0» должна быть выровнена с 8, 5 и 2 над ней.

blockquote>

Поскольку flex-grow не участвует непосредственно в настройках гибких элементов, вам понадобится другой метод.

Вместо этого попробуйте flex-basis. Это работает:

.button    { flex-basis: 33.33%; }
#number0   { flex-basis: calc(66.67% + 10px); }

.numbers {
    display: flex;
    flex-direction: column;
}

.row {
    display: flex;
    flex-direction: row;
    flex-grow: 1;
    justify-content: space-between;
}

.button {
    display: flex;
    flex-basis: 33.33%;
    justify-content: center;
    align-items: center;
    margin: 5px;
    border-radius: 5px;
    border: 1px solid gray;
    background: rgba(255, 255, 255, 0.2);
    cursor: pointer;
}

#number0 {
    flex-basis: calc(66.67% + 10px);
}
1
2
3
4
5
6
7
8
9
0
:

UPDATE

В ответ на обратную связь в комментариях, я сделал корректировку кода выше. Добавляя box-sizing: border-box, корректировка 1-2px исправляется.

.button    { flex-basis: 33.33%; box-sizing: border-box }
#number0   { flex-basis: calc(66.67% + 10px); }

.numbers {
    display: flex;
    flex-direction: column;
}

.row {
    display: flex;
    flex-direction: row;
    flex-grow: 1;
    justify-content: space-between;
}

.button {
    display: flex;
    flex-basis: 33.33%;
    justify-content: center;
    align-items: center;
    margin: 5px;
    border-radius: 5px;
    border: 1px solid gray;
    background: rgba(255, 255, 255, 0.2);
    cursor: pointer;
    box-sizing: border-box;
}

#number0 {
    flex-basis: calc(66.67% + 10px);
}
1
2
3
4
5
6
7
8
9
0
:

JSFIDDLE


Ссылки:

25
задан Rex M 30 April 2009 в 04:40
поделиться

9 ответов

Linq-toXml обрабатывает этот штраф, есть операция XElement.Elements () /. Nodes () для немедленного получения children и операции XElement.Descendents () / DescendentNodes () для получения всех потомков. Можете ли вы рассмотреть это в качестве примера?

Чтобы подвести итог поведения Linq-to-Xml ... Каждая функция навигации соответствует типу оси в XPath ( http://www.w3schools.com/xpath/xpath_axes .asp ). Если функция навигации выбирает Элементы, используется имя оси. Если функция навигации выбирает узлы, имя оси используется с добавленным узлом.

Например, функции Descendants () и DescendantsNode () соответствуют оси потомков XPath, возвращая либо XElement, либо XNode.

The Не удивительно, что исключительный случай - это наиболее часто используемый случай, дочерняя ось. В XPath эта ось используется, если ось не указана. Для этого функции навигации linq-to-xml - это не Children () и ChildrenNodes (), а Elements () и Nodes ().

XElement является подтипом XNode. К XNode относятся такие вещи, как теги HTML, а также комментарии HTML, cdata или текст. XElements являются типом XNode, но относятся конкретно к тегам HTML. Поэтому XElements имеют имя тега и поддерживают функции навигации.

Теперь навигация в Linq-to-XML не так проста, как в XPath. Проблема состоит в том, что функции навигации возвращают объекты коллекции, в то время как функции навигации применяются к не-коллекциям. Рассмотрим выражение XPath, которое выбирает тег таблицы как непосредственный дочерний элемент, а затем любой тег данных таблицы-потомка. Я думаю, что это будет выглядеть как "./children::table/descendants::td" или "./table/descendants ::

20
ответ дан Frank Schwieterman 28 November 2019 в 20:33
поделиться

Ну, первое, что нужно отметить, это то, что на самом деле лямбда-выражения могут быть рекурсивными. Нет, честно! Это нелегко сделать, и , конечно, нелегко читать - черт, у большинства поставщиков LINQ (кроме LINQ-to-Objects, что намного проще) будет кашель, просто глядя на него ... но это возможно . См. Здесь для полной, кровавой детали (предупреждение - боль в мозге, скорее всего).

Однако !! Это, вероятно, не очень поможет ... для практического подхода, я бы посмотрел, как XElement и т. Д. Это делает ... обратите внимание, что вы можете удалить часть рекурсии, используя Queue<T> или Stack<T>:

using System;
using System.Collections.Generic;

static class Program {
    static void Main() {
        Node a = new Node("a"), b = new Node("b") { Children = {a}},
            c = new Node("c") { Children = {b}};
        foreach (Node node in c.Descendents()) {
            Console.WriteLine(node.Name);
        }
    }
}

class Node { // very simplified; no sanity checking etc
    public string Name { get; private set; }
    public List<Node> Children { get; private set; }
    public Node(string name) {
        Name = name;
        Children = new List<Node>();
    }
}
static class NodeExtensions {
    public static IEnumerable<Node> Descendents(this Node node) {
        if (node == null) throw new ArgumentNullException("node");
        if(node.Children.Count > 0) {
            foreach (Node child in node.Children) {
                yield return child;
                foreach (Node desc in Descendents(child)) {
                    yield return desc;
                }
            }
        }
    }
}

Альтернативой было бы написать что-то вроде SelectDeep (чтобы имитировать SelectMany для отдельных уровней):

public static class EnumerableExtensions
{
    public static IEnumerable<T> SelectDeep<T>(
        this IEnumerable<T> source, Func<T, IEnumerable<T>> selector)
    {
        foreach (T item in source)
        {
            yield return item;
            foreach (T subItem in SelectDeep(selector(item),selector))
            {
                yield return subItem;
            }
        }
    }
}
public static class NodeExtensions
{
    public static IEnumerable<Node> Descendents(this Node node)
    {
        if (node == null) throw new ArgumentNullException("node");
        return node.Children.SelectDeep(n => n.Children);
    }
}

Опять же, я не оптимизировал это чтобы избежать рекурсии, но это можно сделать достаточно легко.

19
ответ дан Marc Gravell 15 October 2019 в 15:41
поделиться

Я бы просто реализовал две функции, чтобы четко различать две опции («Дети» против «FullDecendants») или перегрузку GetChildren (bool returnDecendants). Каждый может реализовать IEnumerable, так что было бы просто вопрос, какую функцию они передадут в свой оператор LINQ.

3
ответ дан Reed Copsey 15 October 2019 в 15:41
поделиться

Рекс, ты, конечно, открыл интересную дискуссию, но, похоже, ты упустил все возможности - то есть ты, кажется, отвергаешь как (1) рекурсивную логику потребителя при записи и (2) раскрытие класса своего узла отношения более одного градуса.

Или, возможно, вы не полностью исключили (2). Я могу вспомнить еще один подход, который почти столь же выразителен, как метод (или свойство) GetDescendents, но, возможно, не настолько «тяжелый» (в зависимости от формы вашего дерева) ...

from item in AllItems where item.Parent == currentNode select item

и

from item in AllItems where item.Ancestors.Contains(currentNode) select item
3
ответ дан Av Pinzur 15 October 2019 в 15:41
поделиться

С тобой все в порядке? (это даже не так тяжело)

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace LinqRecursion
{
    class Program
    {
        static void Main(string[] args)
        {
            Person mom = new Person() { Name = "Karen" };
            Person me = new Person(mom) { Name = "Matt" };
            Person youngerBrother = new Person(mom) { Name = "Robbie" };
            Person olderBrother = new Person(mom) { Name = "Kevin" };
            Person nephew1 = new Person(olderBrother) { Name = "Seth" };
            Person nephew2 = new Person(olderBrother) { Name = "Bradon" };
            Person olderSister = new Person(mom) { Name = "Michelle" };

            Console.WriteLine("\tAll");
            //        All
            //Karen 0
            //Matt 1
            //Robbie 2
            //Kevin 3
            //Seth 4
            //Bradon 5
            //Michelle 6
            foreach (var item in mom)
                Console.WriteLine(item);

            Console.WriteLine("\r\n\tOdds");
            //        Odds
            //Matt 1
            //Kevin 3
            //Bradon 5
            var odds = mom.Where(p => p.ID % 2 == 1);
            foreach (var item in odds)
                Console.WriteLine(item);

            Console.WriteLine("\r\n\tEvens");
            //        Evens
            //Karen 0
            //Robbie 2
            //Seth 4
            //Michelle 6
            var evens = mom.Where(p => p.ID % 2 == 0);
            foreach (var item in evens)
                Console.WriteLine(item);

            Console.ReadLine();

        }
    }

    public class Person : IEnumerable<Person>
    {
        private static int _idRoot;

        public Person() {
            _id = _idRoot++;
        }

        public Person(Person parent) : this()
        {
            Parent = parent;
            parent.Children.Add(this);
        }

        private int _id;
        public int ID { get { return _id; } }
        public string Name { get; set; }

        public Person Parent { get; private set; }

        private List<Person> _children;
        public List<Person> Children
        {
            get
            {
                if (_children == null)
                    _children = new List<Person>();
                return _children;
            }
        }

        public override string ToString()
        {
            return Name + " " + _id.ToString();
        }

        #region IEnumerable<Person> Members

        public IEnumerator<Person> GetEnumerator()
        {
            yield return this;
            foreach (var child in this.Children)
                foreach (var item in child)
                    yield return item;
        }

        #endregion

        #region IEnumerable Members

        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }

        #endregion
    }
}
1
ответ дан Matthew Whited 15 October 2019 в 15:41
поделиться

I'd go with implementing it in such a way as to have control over how deeply I want to query as well.

Something like Descendants() would retrieve Descendants through all levels while Descendants(0) would retrieve immediate children, Descendants(1) would get children and grandchildren and so on...

6
ответ дан SirDemon 28 November 2019 в 20:33
поделиться

Я бы согласился с Фрэнком. посмотрим, как LINQ-to-XML обрабатывает эти сценарии.

Фактически, я бы полностью эмулировал реализацию LINQ-to-XML, но изменил бы ее для любого типа данных. Зачем изобретать велосипед правильно?

2
ответ дан andy 28 November 2019 в 20:33
поделиться

Возможно, вы захотите реализовать метод (расширение), например FlattenRecusively, для вашего типа.

from item in list.FlattenRecusively() where ... select item
3
ответ дан 28 November 2019 в 20:33
поделиться

Я бы просто использовал метод расширения для обхода дерева.

Ой, подождите, я делаю , что уже ! :)

0
ответ дан 28 November 2019 в 20:33
поделиться
Другие вопросы по тегам:

Похожие вопросы: