डेपसेट, बेहतर तरीके से काम करने के लिए एक खास डेटा स्ट्रक्चर हैं टारगेट की ट्रांज़िटिव डिपेंडेंसी में डेटा इकट्ठा करना. वे बेहद ज़रूरी हैं नियम प्रोसेसिंग का एक हिस्सा है.
डिप्सेट की सबसे बड़ी खासियत यह है कि इसमें समय और जगह की कम से कम खपत वाले यूनियन ऑपरेशन होते हैं. Depset कंस्ट्रक्टर, एलिमेंट की सूची ("डायरेक्ट") और अन्य एलिमेंट की सूची को स्वीकार करता है डेपसेट ("ट्रांज़िव"), और ऐसा सेट दिखाता है जिसमें सभी वैल्यू शामिल हैं डायरेक्ट एलिमेंट और सभी ट्रांज़िटिव सेट के कॉम्बिनेशन की जानकारी. सैद्धांतिक तौर पर, कंस्ट्रक्टर एक नया ग्राफ़ नोड बनाता है जिसमें डायरेक्ट और ट्रांज़िटिव नोड होते हैं भी ऐसा ही हो सकता है. नीचे दी गई चीज़ों के क्रम के हिसाब से, सिमैंटिक का क्रम अच्छी तरह से तय होता है इस ग्राफ़ की ट्रैवर्सल.
डिपसेट के इस्तेमाल के उदाहरण:
किसी प्रोग्राम की लाइब्रेरी के लिए सभी ऑब्जेक्ट फ़ाइलों के पाथ को स्टोर करना, जो को किसी कंपनी के ज़रिए लिंकर कार्रवाई में भेजा जाएगा.
इंटरप्रेटेड लैंग्वेज के लिए, ऐसी ट्रांज़िटिव सोर्स फ़ाइलों को सेव करना एक एक्ज़ीक्यूटेबल फ़ाइल में शामिल होती है.
ब्यौरा और कार्रवाइयां
सैद्धांतिक रूप से, डिप्सेट एक डायरेक्टेड एकाइक्लिक ग्राफ़ (डीएजी) होता है जो आम तौर पर टारगेट ग्राफ़ से मिलती-जुलती है. इसे पत्तियों से लेकर जड़ तक बनाया जाता है. डिपेंडेंसी चेन का हर टारगेट, पेज के सबसे ऊपर अपना कॉन्टेंट जोड़ सकता है और उन्हें कॉपी किए बिना भी पिछले पेज पर जाएं.
DAG के हर नोड में डायरेक्ट एलिमेंट की सूची और चाइल्ड नोड की सूची होती है. डिप्सेट का कॉन्टेंट, ट्रांज़िटिव एलिमेंट होता है. जैसे, डायरेक्ट एलिमेंट सभी नोड की संख्या में अंतर होता है. नया डेस्क सेट बनाने के लिए, depset कंस्ट्रक्टर: यह डायरेक्ट की सूची को स्वीकार करता है एलिमेंट और चाइल्ड नोड की दूसरी सूची.
s = depset(["a", "b", "c"])
t = depset(["d", "e"], transitive = [s])
print(s) # depset(["a", "b", "c"])
print(t) # depset(["d", "e", "a", "b", "c"])
डिप्सेट का कॉन्टेंट वापस पाने के लिए, to_list() तरीके का इस्तेमाल करें. यह सभी ट्रांज़िटिव की सूची दिखाता है तत्वों में डुप्लीकेट शामिल नहीं हैं. इसका सीधे तौर पर निरीक्षण करने का कोई तरीका नहीं है. डीएजी की सटीक संरचना, हालांकि यह स्ट्रक्चर, कौनसे एलिमेंट दिखाए जाते हैं.
s = depset(["a", "b", "c"])
print("c" in s.to_list()) # True
print(s.to_list() == ["a", "b", "c"]) # True
डिप्सेट में अनुमति वाले आइटम प्रतिबंधित हैं, ठीक उसी तरह जैसे शब्दकोशों पर पाबंदी है. खास तौर पर, ऐसा हो सकता है कि डिप्सेट कॉन्टेंट में बदलाव न किया जा सके.
डिप्सेट रेफ़रंस इक्वलिटी का इस्तेमाल करते हैं: डिप्सेट खुद के बराबर होता है, लेकिन किसी भी वैल्यू के बराबर नहीं होता भले ही उनकी सामग्री एक जैसी हो और उनका स्ट्रक्चर एक जैसा हो.
s = depset(["a", "b", "c"])
t = s
print(s == t) # True
t = depset(["a", "b", "c"])
print(s == t) # False
d = {}
d[s] = None
d[t] = None
print(len(d)) # 2
कॉन्टेंट के हिसाब से डेपसेट की तुलना करने के लिए, उन्हें क्रम से लगाई गई सूचियों में बदलें.
s = depset(["a", "b", "c"])
t = depset(["c", "b", "a"])
print(sorted(s.to_list()) == sorted(t.to_list())) # True
डिपसेट से एलिमेंट हटाने की सुविधा उपलब्ध नहीं है. अगर इसकी ज़रूरत हो, तो को डिप्सेट की पूरी सामग्री पढ़ना होगा, और जिस एलिमेंट को आप चाहते हैं उसे फ़िल्टर करना चाहिए नया डिप्सेट हटाएं और फिर से बनाएं. यह तरीका ज़्यादा कारगर नहीं है.
s = depset(["a", "b", "c"])
t = depset(["b", "c"])
# Compute set difference s - t. Precompute t.to_list() so it's not done
# in a loop, and convert it to a dictionary for fast membership tests.
t_items = {e: None for e in t.to_list()}
diff_items = [x for x in s.to_list() if x not in t_items]
# Convert back to depset if it's still going to be used for union operations.
s = depset(diff_items)
print(s) # depset(["a"])
क्रम
to_list
ऑपरेशन DAG पर ट्रैवर्सल करता है. ट्रैवर्सल का टाइप
उस ऑर्डर पर निर्भर करता है जो डिप्सेट
बनाया गया. कई ऑर्डर के लिए काम करना बेज़ल के लिए फ़ायदेमंद है, क्योंकि कभी-कभी ऐसा होता है कि
टूल अपने इनपुट के क्रम पर ध्यान देते हैं. उदाहरण के लिए, लिंकर कार्रवाई
यह सुनिश्चित करना होगा कि यदि B
पर निर्भर है A
, तो A.o
पहले आने वाला B.o
पहले
लिंकर कमांड लाइन पर क्लिक करें. दूसरे टूल की शर्तें अलग हो सकती हैं.
तीन ट्रैवर्सल ऑर्डर काम करते हैं: postorder
, preorder
, और
topological
. पहले दो बिलकुल पेड़ की तरह काम करते हैं
ट्रैवर्सल
सिर्फ़ डीएजी पर ऑपरेट होते हैं और पहले से विज़िट किए गए नोड को छोड़ देते हैं. तीसरा ऑर्डर
मूल से पत्तियों तक टोपोलॉजिकल सॉर्ट की तरह काम करता है, जो
पहले से ऑर्डर कर सकते हैं, लेकिन शेयर किए गए बच्चे सिर्फ़ उनके माता-पिता के बाद ही सूची में शामिल होते हैं.
प्रीऑर्डर और पोस्टऑर्डर, बाएं से दाएं ट्रैवर्सल के रूप में काम करते हैं, लेकिन ध्यान रखें कि
हर नोड के डायरेक्ट एलिमेंट का बच्चों के हिसाब से कोई क्रम नहीं होता. टोपोलॉजिकल के लिए
ऑर्डर करने पर, कोई लेफ़्ट-टू-राइट गारंटी नहीं मिलती और यहां तक कि
माता-पिता के लिए, बच्चों को मिलने वाली गारंटी तब लागू नहीं होगी, जब
DAG के अलग-अलग नोड में डुप्लीकेट एलिमेंट हैं.
# This demonstrates different traversal orders.
def create(order):
cd = depset(["c", "d"], order = order)
gh = depset(["g", "h"], order = order)
return depset(["a", "b", "e", "f"], transitive = [cd, gh], order = order)
print(create("postorder").to_list()) # ["c", "d", "g", "h", "a", "b", "e", "f"]
print(create("preorder").to_list()) # ["a", "b", "e", "f", "c", "d", "g", "h"]
# This demonstrates different orders on a diamond graph.
def create(order):
a = depset(["a"], order=order)
b = depset(["b"], transitive = [a], order = order)
c = depset(["c"], transitive = [a], order = order)
d = depset(["d"], transitive = [b, c], order = order)
return d
print(create("postorder").to_list()) # ["a", "b", "c", "d"]
print(create("preorder").to_list()) # ["d", "b", "a", "c"]
print(create("topological").to_list()) # ["d", "b", "c", "a"]
ट्रैवर्सल लागू करने के तरीके की वजह से, ऑर्डर के बारे में उसी समय बताया जाना ज़रूरी है
depset को कंस्ट्रक्टर के order
कीवर्ड आर्ग्युमेंट के साथ बनाया गया है. अगर यह
तर्क छोड़ दिया गया हो, लेकिन depset के पास विशेष default
आदेश है, इस मामले में
इसके किसी भी एलिमेंट के क्रम की कोई गारंटी नहीं है (सिवाय इसके कि
सारणिक है).
पूरा उदाहरण
यह उदाहरण यहां उपलब्ध है https://github.com/bazelbuild/examples/tree/main/rules/depsets.
मान लीजिए कि एक काल्पनिक इंटरप्रेटेड भाषा Foo है. बनाने का तरीका
प्रत्येक foo_binary
को उन सभी *.foo
फ़ाइलों के बारे में जानना होगा जिन्हें वह सीधे तौर पर या
अलग-अलग स्थितियों पर निर्भर करता है.
# //depsets:BUILD
load(":foo.bzl", "foo_library", "foo_binary")
# Our hypothetical Foo compiler.
py_binary(
name = "foocc",
srcs = ["foocc.py"],
)
foo_library(
name = "a",
srcs = ["a.foo", "a_impl.foo"],
)
foo_library(
name = "b",
srcs = ["b.foo", "b_impl.foo"],
deps = [":a"],
)
foo_library(
name = "c",
srcs = ["c.foo", "c_impl.foo"],
deps = [":a"],
)
foo_binary(
name = "d",
srcs = ["d.foo"],
deps = [":b", ":c"],
)
# //depsets:foocc.py
# "Foo compiler" that just concatenates its inputs to form its output.
import sys
if __name__ == "__main__":
assert len(sys.argv) >= 1
output = open(sys.argv[1], "wt")
for path in sys.argv[2:]:
input = open(path, "rt")
output.write(input.read())
यहां, बाइनरी d
के ट्रांज़िटिव सोर्स, इसमें मौजूद सभी *.foo
फ़ाइलें हैं
a
, b
, c
, और d
के srcs
फ़ील्ड. foo_binary
के लिए ऑर्डर
d.foo
के अलावा किसी अन्य फ़ाइल के बारे में जानने के लिए, foo_library
लक्ष्यों के लिए
उन्हें एक ही सेवा देने वाली कंपनी के पास भेज सकते हैं. हर लाइब्रेरी अपने-आप ऐसेट उपलब्ध कराती है
डिपेंडेंसी, अपने ही मौजूदा सोर्स को जोड़ता है, और एक नई कंपनी को पास करता है.
ऑगमेंटेड कॉन्टेंट देखें. foo_binary
नियम यही काम करता है, लेकिन इसके बजाय यह लागू होता है
वापस लौटाने की, यह एक बनाने के लिए स्रोतों की पूरी सूची का उपयोग करता है
कमांड लाइन जोड़ें.
यहां foo_library
और foo_binary
नियमों को पूरी तरह से लागू करने की जानकारी दी गई है.
# //depsets/foo.bzl
# A provider with one field, transitive_sources.
FooFiles = provider(fields = ["transitive_sources"])
def get_transitive_srcs(srcs, deps):
"""Obtain the source files for a target and its transitive dependencies.
Args:
srcs: a list of source files
deps: a list of targets that are direct dependencies
Returns:
a collection of the transitive sources
"""
return depset(
srcs,
transitive = [dep[FooFiles].transitive_sources for dep in deps])
def _foo_library_impl(ctx):
trans_srcs = get_transitive_srcs(ctx.files.srcs, ctx.attr.deps)
return [FooFiles(transitive_sources=trans_srcs)]
foo_library = rule(
implementation = _foo_library_impl,
attrs = {
"srcs": attr.label_list(allow_files=True),
"deps": attr.label_list(),
},
)
def _foo_binary_impl(ctx):
foocc = ctx.executable._foocc
out = ctx.outputs.out
trans_srcs = get_transitive_srcs(ctx.files.srcs, ctx.attr.deps)
srcs_list = trans_srcs.to_list()
ctx.actions.run(executable = foocc,
arguments = [out.path] + [src.path for src in srcs_list],
inputs = srcs_list + [foocc],
outputs = [out])
foo_binary = rule(
implementation = _foo_binary_impl,
attrs = {
"srcs": attr.label_list(allow_files=True),
"deps": attr.label_list(),
"_foocc": attr.label(default=Label("//depsets:foocc"),
allow_files=True, executable=True, cfg="host")
},
outputs = {"out": "%{name}.out"},
)
आप इन फ़ाइलों को किसी नए पैकेज में कॉपी करके, इसकी जाँच कर सकते हैं
सही तरीके से लेबल कर सकता है, डमी कॉन्टेंट वाली सोर्स *.foo
फ़ाइलें बना सकता है, और
d
का टारगेट बनाना.
परफ़ॉर्मेंस
डिपसेट इस्तेमाल करने की वजह जानने के लिए, सोचें कि आपके
get_transitive_srcs()
ने अपने सोर्स एक सूची में इकट्ठा किए.
def get_transitive_srcs(srcs, deps):
trans_srcs = []
for dep in deps:
trans_srcs += dep[FooFiles].transitive_sources
trans_srcs += srcs
return trans_srcs
इसमें डुप्लीकेट पर ध्यान नहीं दिया जाता है, इसलिए a
की सोर्स फ़ाइलें
कमांड लाइन पर दो बार और आउटपुट के कॉन्टेंट में दो बार दिखेगा
फ़ाइल से लिए जाते हैं.
एक विकल्प, सामान्य सेट का इस्तेमाल कर रहा है, जिसे
डिक्शनरी में बटन, एलिमेंट होते हैं और सभी बटन True
से मैप होते हैं.
def get_transitive_srcs(srcs, deps):
trans_srcs = {}
for dep in deps:
for file in dep[FooFiles].transitive_sources:
trans_srcs[file] = True
for file in srcs:
trans_srcs[file] = True
return trans_srcs
इससे डुप्लीकेट से छुटकारा मिल जाता है, लेकिन यह कमांड लाइन का क्रम तय कर देता है तर्क (और इसलिए फ़ाइलों की सामग्री) की जानकारी नहीं दी गई है, हालांकि सारणिक.
इसके अलावा, दोनों ही तरीके डिप्सेट-आधारित तरीकों की तुलना में असामान्य रूप से खराब हैं अप्रोच का इस्तेमाल करें. ऐसे मामले पर विचार करें जहां निर्भरता की एक लंबी चेन हो फ़ू लाइब्रेरी. हर नियम को प्रोसेस करने के लिए, सभी ट्रांज़िटिव को कॉपी करना ज़रूरी है नए डेटा स्ट्रक्चर में शामिल होने से पहले मिले सोर्स. इसका मतलब है कि किसी एक लाइब्रेरी या बाइनरी टारगेट का विश्लेषण करने में लगने वाला समय और जगह शृंखला में इसकी ऊंचाई के समानुपात है. n लंबाई की चेन के लिए, foolib_1 ← foolib_2 ← ... ← foolib_n, कुल लागत प्रभावी रूप से O(n^2).
आम तौर पर, डेटा इकट्ठा करते समय डिपसेट का इस्तेमाल किया जाना चाहिए डिपेंडेंसी के तौर पर आपके डेटा का इस्तेमाल करता है. इससे यह पक्का करने में मदद मिलती है कि बड़ी संख्या में आपका बिल्ड स्केल बड़ा हो जाएगा.
आखिर में, यह ज़रूरी है कि डिपसेट का कॉन्टेंट वापस न लिया जाए
बिना किसी वजह से लागू किए जा रहे हैं. to_list()
पर एक कॉल
वैल्यू के आखिर में बाइनरी नियम बनाया जा सकता है, क्योंकि कुल लागत सिर्फ़ O(n) है. यह
जब कई नॉन-टर्मिनल टारगेट, द्विघात व्यवहार to_list()
को कॉल करने की कोशिश करते हैं
होता है.
डिपसेट का बेहतर तरीके से इस्तेमाल करने के बारे में ज़्यादा जानकारी के लिए, परफ़ॉर्मेंस पेज देखें.
एपीआई का संदर्भ
ज़्यादा जानकारी के लिए, कृपया यहां जाएं.