Class: RDF::N3::List

Inherits:
List
  • Object
show all
Includes:
Term
Defined in:
lib/rdf/n3/list.rb

Overview

Sub-class of RDF::List which uses a native representation of values and allows recursive lists.

Also serves as the vocabulary URI for expanding other methods

Constant Summary collapse

URI =
RDF::URI("http://www.w3.org/2000/10/swap/list#")

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Term

#as_datetime, #as_number, #sameTerm?

Methods inherited from List

#to_sxp

Constructor Details

#initialize(subject: nil, graph: nil, values: nil) {|list| ... } ⇒ List

Initializes a newly-constructed list.

Instantiates a new list based at ‘subject`, which must be an RDF::Node. List may be initialized using passed `values`.

If values is not provided, but subject and graph are, then will attempt to recursively represent lists.

Examples:

add constructed list to existing graph

l = RDF::N3::List(values: (1, 2, 3))
g = RDF::Graph.new << l
g.count # => l.count

Parameters:

  • subject (RDF::Resource) (defaults to: nil)

    (RDF.nil) Subject should be an RDF::Node, not a URI. A list with an IRI head will not validate, but is commonly used to detect if a list is valid.

  • graph (RDF::Graph) (defaults to: nil)

    (RDF::Graph.new)

  • values (Array<RDF::Term>) (defaults to: nil)

    Any values which are not terms are coerced to ‘RDF::Literal`.

Yields:

  • (list)

Yield Parameters:



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/rdf/n3/list.rb', line 64

def initialize(subject: nil, graph: nil, values: nil, &block)
  @subject = subject || (Array(values).empty? ? RDF.nil : RDF::Node.new)
  @graph = graph
  @valid = true

  @values = case
  when values
    values.map do |v|
      # Convert values, as necessary.
      case v
      when RDF::Value then v.to_term
      when Symbol     then RDF::Node.intern(v)
      when Array      then RDF::N3::List.new(values: v)
      when nil        then RDF.nil
      else                 RDF::Literal.new(v)
      end
    end
  when subject && graph
    ln = RDF::List.new(subject: subject, graph: graph)
    @valid = ln.valid?
    ln.to_a.map {|li| self.class.try_list(li, graph)}
  else
    []
  end
end

Class Method Details

.method_missing(property, *args, &block) ⇒ Object

Returns a vocubulary term



13
14
15
16
17
18
19
20
# File 'lib/rdf/n3/list.rb', line 13

def self.method_missing(property, *args, &block)
  property = RDF::Vocabulary.camelize(property.to_s)
  if args.empty? && !to_s.empty?
    RDF::Vocabulary::Term.intern("#{URI}#{property}", attributes: {})
  else
    super
  end
end

.to_uriURI

Returns the base URI for this vocabulary.

Returns:



26
27
28
# File 'lib/rdf/n3/list.rb', line 26

def self.to_uri
  URI
end

.try_list(subject, graph) ⇒ RDF::List, RDF::Resource

Attempts to create an RDF::N3::List from subject, or returns the node as is, if unable.

Parameters:

  • subject (RDF::Resource)

Returns:

  • (RDF::List, RDF::Resource)

    returns either the original resource, or a list based on that resource



35
36
37
38
39
40
41
42
43
# File 'lib/rdf/n3/list.rb', line 35

def self.try_list(subject, graph)
  return subject unless subject && (subject.node? || subject.uri? && subject == RDF.nil)
  ln = RDF::List.new(subject: subject, graph: graph)
  return subject unless ln.valid?

  # Return a new list, outside of this queryable, with any embedded lists also expanded
  values = ln.to_a.map {|li| try_list(li, graph)}
  RDF::N3::List.new(subject: subject, graph: graph, values: values)
end

Instance Method Details

#<<(value) ⇒ RDF::List

Appends an element to the tail of this list.

Examples:

RDF::List[] << 1 << 2 << 3              #=> RDF::List[1, 2, 3]

Parameters:

Returns:

See Also:



240
241
242
243
244
245
# File 'lib/rdf/n3/list.rb', line 240

def <<(value)
  value = normalize_value(value)
  @subject = nil
  @values << value
  self
end

#==(other) ⇒ Object

See Also:

  • Value#==


98
99
100
101
102
103
104
# File 'lib/rdf/n3/list.rb', line 98

def ==(other)
  case other
  when Array, RDF::List then to_a == other.to_a
  else
    false
  end
end

#[]=(index, term) ⇒ RDF::Term #[]=(start, length, value) ⇒ RDF::Term, RDF::List #[]=(range, value) ⇒ RDF::Term, RDF::List

Element Assignment — Sets the element at ‘index`, or replaces a subarray from the `start` index for `length` elements, or replaces a subarray specified by the `range` of indices.

Overloads:

  • #[]=(index, term) ⇒ RDF::Term

    Replaces the element at ‘index` with `term`.

    Parameters:

    • index (Integer)
    • term (RDF::Term)

      A non-RDF::Term is coerced to a Literal.

    Returns:

    Raises:

    • (IndexError)
  • #[]=(start, length, value) ⇒ RDF::Term, RDF::List

    Replaces a subarray from the ‘start` index for `length` elements with `value`. Value is a Term, Array of Term, or List.

    Parameters:

    Returns:

    Raises:

    • (IndexError)
  • #[]=(range, value) ⇒ RDF::Term, RDF::List

    Replaces a subarray from the ‘start` index for `length` elements with `value`. Value is a Term, Array of Term, or List.

    Parameters:

    Returns:

    Raises:

    • (IndexError)


141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'lib/rdf/n3/list.rb', line 141

def []=(*args)
  value = case args.last
  when Array then args.last
  when RDF::List then args.last.to_a
  else [args.last]
  end.map do |v|
    # Convert values, as necessary.
    case v
    when RDF::Value then v.to_term
    when Symbol     then RDF::Node.intern(v)
    when Array      then RDF::N3::List.new(values: v)
    when nil        then RDF.nil
    else                 RDF::Literal.new(v)
    end
  end

  ret = case args.length
  when 3
    start, length = args[0], args[1]
    @subject = nil if start == 0
    @values[start, length] = value
  when 2
    case args.first
    when Integer
      raise ArgumentError, "Index form of []= takes a single term" if args.last.is_a?(Array)
      @values[args.first] = value.first
    when Range
      @values[args.first] = value
    else
      raise ArgumentError, "Index form of must use an integer or range"
    end
  else
    raise ArgumentError, "List []= takes one or two index values"
  end

  # Fill any nil entries in @values with rdf:nil
  @values.map! {|v| v || RDF.nil}

  @subject = RDF.nil if @values.empty?
  @subject ||= RDF::Node.new
  ret # Returns inserted values
end

#at(index) ⇒ RDF::Term?

Returns the element at ‘index`.

Examples:

RDF::List[1, 2, 3].at(0)                #=> 1
RDF::List[1, 2, 3].at(4)                #=> nil

Returns:

See Also:



312
313
314
# File 'lib/rdf/n3/list.rb', line 312

def at(index)
  @values.at(index)
end

#clearRDF::List

Empties this list

Examples:

RDF::List[1, 2, 2, 3].clear    #=> RDF::List[]

Returns:

See Also:



225
226
227
228
229
# File 'lib/rdf/n3/list.rb', line 225

def clear
  @values.clear
  @subject = nil
  self
end

#each(&block) ⇒ Enumerator

Yields each element in this list.

Examples:

RDF::List[1, 2, 3].each do |value|
  puts value.inspect
end

Returns:

  • (Enumerator)

See Also:



371
372
373
374
375
# File 'lib/rdf/n3/list.rb', line 371

def each(&block)
  return to_enum unless block_given?

  @values.each(&block)
end

#each_descendant {|term| ... } ⇒ Enumerator

Enumerate via depth-first recursive descent over list members, yielding each member

Yields:

  • term

Yield Parameters:

Returns:

  • (Enumerator)


434
435
436
437
438
439
440
441
442
# File 'lib/rdf/n3/list.rb', line 434

def each_descendant(&block)
  if block_given?
    each do |term|
      term.each_descendant(&block) if term.list?
      block.call(term)
    end
  end
  enum_for(:each_descendant)
end

#each_statement(&block) ⇒ Enumerator

Yields each statement constituting this list. Uses actual statements if a graph was set, otherwise, the saved values.

This will recursively get statements for sub-lists as well.

Examples:

RDF::List[1, 2, 3].each_statement do |statement|
  puts statement.inspect
end

Returns:

  • (Enumerator)

See Also:

  • Enumerable#each_statement


389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
# File 'lib/rdf/n3/list.rb', line 389

def each_statement(&block)
  return enum_statement unless block_given?

  if graph
    RDF::List.new(subject: subject, graph: graph).each_statement(&block)
  elsif @values.length > 0
    # Create a subject for each entry based on the subject bnode
    subjects = (0..(@values.count-1)).map {|ndx| ndx > 0 ? RDF::Node.intern("#{subject.id}_#{ndx}") : subject}
    *values, last = @values
    while !values.empty?
      subj = subjects.shift
      value = values.shift
      block.call(RDF::Statement(subj, RDF.first, value.list? ? value.subject : value))
      block.call(RDF::Statement(subj, RDF.rest, subjects.first))
    end
    subj = subjects.shift
    block.call(RDF::Statement(subj, RDF.first, last.list? ? last.subject : last))
    block.call(RDF::Statement(subj, RDF.rest, RDF.nil))
  end

  # If a graph was used, also get statements from sub-lists
  @values.select(&:list?).each {|li| li.each_statement(&block)}
end

#each_subject(&block) ⇒ Enumerator

Yields each subject term constituting this list along with sub-lists.

Examples:

RDF::List[1, 2, 3].each_subject do |subject|
  puts subject.inspect
end

Returns:

  • (Enumerator)

See Also:

  • Enumerable#each


423
424
425
426
427
# File 'lib/rdf/n3/list.rb', line 423

def each_subject(&block)
  return enum_subject unless block_given?

  each_statement {|st| block.call(st.subject) if st.predicate == RDF.rest}
end

#empty?Boolean

Returns ‘true` if this list is empty.

Examples:

RDF::List[].empty?                      #=> true
RDF::List[1, 2, 3].empty?               #=> false

Returns:

  • (Boolean)

See Also:



256
257
258
# File 'lib/rdf/n3/list.rb', line 256

def empty?
  @values.empty?
end

#eql?(other) ⇒ Boolean

Checks pattern equality against another list, considering nesting.

Parameters:

  • other (List, Array)

Returns:

  • (Boolean)


485
486
487
488
489
490
491
492
493
494
495
496
497
# File 'lib/rdf/n3/list.rb', line 485

def eql?(other)
  other = RDF::N3::List[*other] if other.is_a?(Array)
  return false if !other.is_a?(RDF::List) || count != other.count
  @values.each_with_index do |li, ndx|
    case li
    when RDF::Query::Pattern, RDF::N3::List
      return false unless li.eql?(other.at(ndx))
    else
      return false unless li == other.at(ndx)
    end
  end
  true
end

#evaluate(bindings, formulae: {}, **options) ⇒ RDF::N3::List

Evaluates the list using the given variable ‘bindings`.

Parameters:

  • bindings (Hash{Symbol => RDF::Term})

    a query solution containing zero or more variable bindings

  • options (Hash{Symbol => Object})

    ({}) options passed from query

Returns:

See Also:

  • SPARQL::Algebra::Expression.evaluate


559
560
561
562
563
564
565
566
567
568
569
# File 'lib/rdf/n3/list.rb', line 559

def evaluate(bindings, formulae: {}, **options)
  # if values are constant, simply return ourselves
  return self if to_a.none? {|li| li.node? || li.variable?}
  bindings = bindings.to_h unless bindings.is_a?(Hash)
  # Create a new list subject using a combination of the current subject and a hash of the binding values
  subj = "#{subject.id}_#{bindings.values.sort.hash}"
  values = to_a.map do |o|
    o = o.evaluate(bindings, formulae: formulae, **options) || o
  end
  RDF::N3::List.new(subject: RDF::Node.intern(subj), values: values)
end

#fetch(*args, &block) ⇒ RDF::Term?

Returns element at ‘index` with default.

Examples:

RDF::List[1, 2, 3].fetch(0)             #=> RDF::Literal(1)
RDF::List[1, 2, 3].fetch(4)             #=> IndexError
RDF::List[1, 2, 3].fetch(4, nil)        #=> nil
RDF::List[1, 2, 3].fetch(4) { |n| n*n } #=> 16

Returns:

See Also:



299
300
301
# File 'lib/rdf/n3/list.rb', line 299

def fetch(*args, &block)
  @values.fetch(*args, &block)
end

#firstRDF::Term

Returns the first element in this list.

Examples:

RDF::List[*(1..10)].first               #=> RDF::Literal(1)

Returns:



323
324
325
# File 'lib/rdf/n3/list.rb', line 323

def first
  @values.first
end

#has_nodes?Boolean

Does this list, or any recusive list have any blank node members?

Returns:

  • (Boolean)


448
449
450
# File 'lib/rdf/n3/list.rb', line 448

def has_nodes?
  @values.any? {|e| e.node? || e.list? && e.has_nodes?}
end

#hashObject

The list hash is the hash of it’s members.

See Also:

  • Value#hash


110
111
112
# File 'lib/rdf/n3/list.rb', line 110

def hash
  to_a.hash
end

#index(value) ⇒ Integer

Returns the index of the first element equal to ‘value`, or `nil` if no match was found.

Examples:

RDF::List['a', 'b', 'c'].index('a')     #=> 0
RDF::List['a', 'b', 'c'].index('d')     #=> nil

Parameters:

Returns:

  • (Integer)

See Also:



284
285
286
# File 'lib/rdf/n3/list.rb', line 284

def index(value)
  @values.index(value)
end

#lastRDF::Term

Returns the last element in this list.

Examples:

RDF::List[*(1..10)].last                 #=> RDF::Literal(10)

Returns:

See Also:



335
336
337
# File 'lib/rdf/n3/list.rb', line 335

def last
  @values.last
end

#lengthInteger

Returns the length of this list.

Examples:

RDF::List[].length                      #=> 0
RDF::List[1, 2, 3].length               #=> 3

Returns:

  • (Integer)

See Also:



269
270
271
# File 'lib/rdf/n3/list.rb', line 269

def length
  @values.length
end

#restRDF::List

Returns a list containing all but the first element of this list.

Examples:

RDF::List[1, 2, 3].rest                 #=> RDF::List[2, 3]

Returns:



346
347
348
# File 'lib/rdf/n3/list.rb', line 346

def rest
  self.class.new(values: @values[1..-1])
end

#shiftRDF::Term

Removes and returns the element at the head of this list.

Examples:

RDF::List[1,2,3].shift              #=> 1

Returns:

See Also:



211
212
213
214
215
# File 'lib/rdf/n3/list.rb', line 211

def shift
  return nil if empty?
  @subject = nil
  @values.shift
end

#solution(list) ⇒ RDF::Query::Solution

Returns a query solution constructed by binding any variables in this list with the corresponding terms in the given ‘list`.

Parameters:

Returns:

See Also:

  • Query::Pattern#solution


578
579
580
581
582
583
584
585
586
587
588
# File 'lib/rdf/n3/list.rb', line 578

def solution(list)
  RDF::Query::Solution.new do |solution|
    @values.each_with_index do |li, ndx|
      if li.respond_to?(:solution)
        solution.merge!(li.solution(list[ndx]))
      elsif li.is_a?(RDF::Query::Variable)
        solution[li.to_sym] = list[ndx]
      end
    end
  end
end

#tailRDF::List

Returns a list containing the last element of this list.

Examples:

RDF::List[1, 2, 3].tail                 #=> RDF::List[3]

Returns:



357
358
359
# File 'lib/rdf/n3/list.rb', line 357

def tail
  self.class.new(values: @values[-1..-1])
end

#to_aArray

Returns the elements in this list as an array.

Examples:

RDF::List[].to_a                        #=> []
RDF::List[1, 2, 3].to_a                 #=> [RDF::Literal(1), RDF::Literal(2), RDF::Literal(3)]

Returns:

  • (Array)


476
477
478
# File 'lib/rdf/n3/list.rb', line 476

def to_a
  @values
end

#to_baseSring

Returns the base representation of this term.

Returns:

  • (Sring)


594
595
596
# File 'lib/rdf/n3/list.rb', line 594

def to_base
  "(#{@values.map(&:to_base).join(' ')})"
end

#to_ndvar(scope) ⇒ RDF::N3::List

Substitutes blank node members with existential variables, recusively.

Parameters:

Returns:



457
458
459
460
461
462
463
464
465
466
# File 'lib/rdf/n3/list.rb', line 457

def to_ndvar(scope)
  values = @values.map do |e|
    case e
    when RDF::Node     then e.to_ndvar(scope)
    when RDF::N3::List then e.to_ndvar(scope)
    else                    e
    end
  end
  RDF::N3::List.new(values: values)
end

#to_sxp_binArray

Transform Statement into an SXP

Returns:

  • (Array)


600
601
602
# File 'lib/rdf/n3/list.rb', line 600

def to_sxp_bin
  to_a.to_sxp_bin
end

#transform(&block) ⇒ RDF::N3::list

Creates a new list by recusively mapping the values of the list

Returns:

  • (RDF::N3::list)


608
609
610
611
# File 'lib/rdf/n3/list.rb', line 608

def transform(&block)
  values = self.to_a.map {|v| v.list? ? v.map(&block) : block.call(v)}
  RDF::N3::List.new(values: values)
end

#unshift(value) ⇒ RDF::List

Appends an element to the head of this list. Existing references are not updated, as the list subject changes as a side-effect.

Examples:

RDF::List[].unshift(1).unshift(2).unshift(3) #=> RDF::List[3, 2, 1]

Parameters:

Returns:

See Also:



195
196
197
198
199
200
201
# File 'lib/rdf/n3/list.rb', line 195

def unshift(value)
  value = normalize_value(value)
  @values.unshift(value)
  @subject = nil

  return self
end

#valid?Boolean

Lists are valid, unless established via RDF::List, in which case they are only valid if the RDF::List is valid.

Returns:

  • (Boolean)


94
# File 'lib/rdf/n3/list.rb', line 94

def valid?; @valid; end

#var_values(var, list) ⇒ Array<RDF::Term>

Returns all values the list in the same pattern position

Parameters:

Returns:



540
541
542
543
544
545
546
547
548
# File 'lib/rdf/n3/list.rb', line 540

def var_values(var, list)
  results = []
  @values.each_index do |ndx|
    maybe_var = @values[ndx]
    next unless maybe_var.respond_to?(:var_values)
    results.push(*Array(maybe_var.var_values(var, list.at(ndx))))
  end
  results.flatten.compact
end

#variable?Boolean

A list is variable if any of its members are variable?

Returns:

  • (Boolean)


503
504
505
# File 'lib/rdf/n3/list.rb', line 503

def variable?
  @values.any?(&:variable?)
end

#variable_countInteger

Returns the number of variables in this list, recursively.

Returns:

  • (Integer)


530
531
532
# File 'lib/rdf/n3/list.rb', line 530

def variable_count
  variables.length
end

#variablesHash{Symbol => Variable}

Returns all variables in this list.

Note: this returns a hash containing distinct variables only.

Returns:

  • (Hash{Symbol => Variable})


520
521
522
523
524
# File 'lib/rdf/n3/list.rb', line 520

def variables
  @values.inject({}) do |hash, li|
    li.respond_to?(:variables) ? hash.merge(li.variables) : hash
  end
end

#varsArray<RDF::Query::Variable>

Return the variables contained this list

Returns:



510
511
512
# File 'lib/rdf/n3/list.rb', line 510

def vars
  @values.vars
end